/* * Copyright 2012 * Ubiquitous Knowledge Processing (UKP) Lab and FG Language Technology * Technische Universität Darmstadt * <p> * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package de.tudarmstadt.ukp.clarin.webanno.ui.curation.component; import static de.tudarmstadt.ukp.clarin.webanno.api.annotation.util.TypeUtil.getAdapter; import static de.tudarmstadt.ukp.clarin.webanno.api.annotation.util.WebAnnoCasUtil.findWindowStartCenteringOnSelection; import static de.tudarmstadt.ukp.clarin.webanno.api.annotation.util.WebAnnoCasUtil.getSentenceNumber; import static de.tudarmstadt.ukp.clarin.webanno.api.annotation.util.WebAnnoCasUtil.selectByAddr; import static de.tudarmstadt.ukp.clarin.webanno.api.annotation.util.WebAnnoCasUtil.selectSentenceAt; import java.io.IOException; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.apache.uima.UIMAException; import org.apache.uima.cas.ArrayFS; import org.apache.uima.cas.Feature; import org.apache.uima.cas.FeatureStructure; import org.apache.uima.cas.Type; import org.apache.uima.cas.text.AnnotationFS; import org.apache.uima.jcas.JCas; import org.apache.wicket.MarkupContainer; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.markup.html.WebMarkupContainer; import org.apache.wicket.markup.html.list.ListItem; import org.apache.wicket.markup.html.list.ListView; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import org.apache.wicket.request.IRequestParameters; import org.apache.wicket.spring.injection.annot.SpringBean; import org.apache.wicket.util.string.StringValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.context.SecurityContextHolder; import com.fasterxml.jackson.core.JsonGenerator; import de.tudarmstadt.ukp.clarin.webanno.api.AnnotationSchemaService; import de.tudarmstadt.ukp.clarin.webanno.api.CorrectionDocumentService; import de.tudarmstadt.ukp.clarin.webanno.api.DocumentService; import de.tudarmstadt.ukp.clarin.webanno.api.WebAnnoConst; import de.tudarmstadt.ukp.clarin.webanno.api.annotation.AnnotationEditorBase; import de.tudarmstadt.ukp.clarin.webanno.api.annotation.adapter.TypeAdapter; import de.tudarmstadt.ukp.clarin.webanno.api.annotation.coloring.ColoringStrategy; import de.tudarmstadt.ukp.clarin.webanno.api.annotation.exception.AnnotationException; import de.tudarmstadt.ukp.clarin.webanno.api.annotation.model.AnnotatorState; import de.tudarmstadt.ukp.clarin.webanno.api.annotation.util.TypeUtil; import de.tudarmstadt.ukp.clarin.webanno.brat.adapter.TypeRenderer; import de.tudarmstadt.ukp.clarin.webanno.brat.message.GetCollectionInformationResponse; import de.tudarmstadt.ukp.clarin.webanno.brat.message.GetDocumentResponse; import de.tudarmstadt.ukp.clarin.webanno.brat.render.BratRenderer; import de.tudarmstadt.ukp.clarin.webanno.brat.util.BratAnnotatorUtility; import de.tudarmstadt.ukp.clarin.webanno.curation.casdiff.CasDiff2; import de.tudarmstadt.ukp.clarin.webanno.curation.casdiff.CasDiff2.Configuration; import de.tudarmstadt.ukp.clarin.webanno.curation.casdiff.CasDiff2.ConfigurationSet; import de.tudarmstadt.ukp.clarin.webanno.curation.casdiff.CasDiff2.DiffResult; import de.tudarmstadt.ukp.clarin.webanno.curation.casdiff.CasDiff2.LinkCompareBehavior; import de.tudarmstadt.ukp.clarin.webanno.curation.storage.CurationDocumentService; import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationDocument; import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationDocumentState; import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationFeature; import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationLayer; import de.tudarmstadt.ukp.clarin.webanno.model.Mode; import de.tudarmstadt.ukp.clarin.webanno.model.ScriptDirection; import de.tudarmstadt.ukp.clarin.webanno.model.SourceDocument; import de.tudarmstadt.ukp.clarin.webanno.security.UserDao; import de.tudarmstadt.ukp.clarin.webanno.security.model.User; import de.tudarmstadt.ukp.clarin.webanno.support.JSONUtil; import de.tudarmstadt.ukp.clarin.webanno.ui.curation.component.model.AnnotationOption; import de.tudarmstadt.ukp.clarin.webanno.ui.curation.component.model.AnnotationSelection; import de.tudarmstadt.ukp.clarin.webanno.ui.curation.component.model.AnnotationState; import de.tudarmstadt.ukp.clarin.webanno.ui.curation.component.model.BratSuggestionVisualizer; import de.tudarmstadt.ukp.clarin.webanno.ui.curation.component.model.CurationContainer; import de.tudarmstadt.ukp.clarin.webanno.ui.curation.component.model.CurationUserSegmentForAnnotationDocument; import de.tudarmstadt.ukp.clarin.webanno.ui.curation.component.model.SourceListView; import de.tudarmstadt.ukp.clarin.webanno.ui.curation.component.model.SuggestionBuilder; import de.tudarmstadt.ukp.clarin.webanno.ui.curation.util.MergeCas; import de.tudarmstadt.ukp.dkpro.core.api.segmentation.type.Sentence; import de.tudarmstadt.ukp.dkpro.core.api.segmentation.type.Token; /** * A {@link MarkupContainer} for either curation users' sentence annotation (for the lower panel) or * the automated annotations */ public class SuggestionViewPanel extends WebMarkupContainer { private static final long serialVersionUID = 8736268179612831795L; private static final Logger LOG = LoggerFactory.getLogger(SuggestionViewPanel.class); private final ListView<CurationUserSegmentForAnnotationDocument> sentenceListView; private @SpringBean DocumentService documentService; private @SpringBean CurationDocumentService curationDocumentService; private @SpringBean CorrectionDocumentService correctionDocumentService; private @SpringBean AnnotationSchemaService annotationService; private @SpringBean UserDao userRepository; /** * Data models for the annotation editor * * @param aModel the model. */ public void setModel(IModel<LinkedList<CurationUserSegmentForAnnotationDocument>> aModel) { setDefaultModel(aModel); } public void setModelObject(LinkedList<CurationUserSegmentForAnnotationDocument> aModel) { setDefaultModelObject(aModel); } @SuppressWarnings("unchecked") public IModel<LinkedList<CurationUserSegmentForAnnotationDocument>> getModel() { return (IModel<LinkedList<CurationUserSegmentForAnnotationDocument>>) getDefaultModel(); } @SuppressWarnings("unchecked") public LinkedList<CurationUserSegmentForAnnotationDocument> getModelObject() { return (LinkedList<CurationUserSegmentForAnnotationDocument>) getDefaultModelObject(); } public SuggestionViewPanel(String id, IModel<LinkedList<CurationUserSegmentForAnnotationDocument>> aModel) { super(id, aModel); // update list of brat embeddings sentenceListView = new ListView<CurationUserSegmentForAnnotationDocument>( "sentenceListView", aModel) { private static final long serialVersionUID = -5389636445364196097L; @Override protected void populateItem( ListItem<CurationUserSegmentForAnnotationDocument> item2) { final CurationUserSegmentForAnnotationDocument curationUserSegment = item2 .getModelObject(); BratSuggestionVisualizer curationVisualizer = new BratSuggestionVisualizer( "sentence", new Model<CurationUserSegmentForAnnotationDocument>(curationUserSegment)) { private static final long serialVersionUID = -1205541428144070566L; /** * Method is called, if user has clicked on a span or an arc in the sentence * panel. The span or arc respectively is identified and copied to the merge * cas. */ @Override protected void onSelectAnnotationForMerge(AjaxRequestTarget aTarget) throws UIMAException, ClassNotFoundException, IOException, AnnotationException { // TODO: chain the error from this component up in the // CurationPage // or CorrectionPage if (BratAnnotatorUtility.isDocumentFinished(documentService, curationUserSegment.getBratAnnotatorModel())) { aTarget.appendJavaScript("alert('This document is already closed." + " Please ask admin to re-open')"); return; } final IRequestParameters request = getRequest().getPostParameters(); String username = SecurityContextHolder.getContext().getAuthentication() .getName(); User user = userRepository.get(username); SourceDocument sourceDocument = curationUserSegment.getBratAnnotatorModel() .getDocument(); JCas annotationJCas = null; annotationJCas = (curationUserSegment.getBratAnnotatorModel().getMode() .equals(Mode.AUTOMATION) || curationUserSegment .getBratAnnotatorModel().getMode().equals(Mode.CORRECTION)) ? documentService.readAnnotationCas( documentService.getAnnotationDocument(sourceDocument, user)) : curationDocumentService.readCurationCas(sourceDocument); StringValue action = request.getParameterValue("action"); // check if clicked on a span if (!action.isEmpty() && action.toString().equals("selectSpanForMerge")) { mergeSpan(request, curationUserSegment, annotationJCas); } // check if clicked on an arc else if (!action.isEmpty() && action.toString() .equals("selectArcForMerge")) { // add span for merge // get information of the span clicked mergeArc(request, curationUserSegment, annotationJCas); } onChange(aTarget); } }; curationVisualizer.setOutputMarkupId(true); item2.add(curationVisualizer); } }; sentenceListView.setOutputMarkupId(true); add(sentenceListView); } boolean isCorefType(AnnotationFS aFS) { for (Feature f : MergeCas.getAllFeatures(aFS)) { if (f.getShortName().equals(WebAnnoConst.COREFERENCE_RELATION_FEATURE) || f.getShortName().equals(WebAnnoConst.COREFERENCE_TYPE_FEATURE)) { return true; } } return false; } protected void onChange(AjaxRequestTarget aTarget) { // Overriden in curationPanel } protected void isCorrection(AjaxRequestTarget aTarget) { // Overriden in curationPanel } private void mergeSpan(IRequestParameters aRequest, CurationUserSegmentForAnnotationDocument aCurationUserSegment, JCas aJcas) throws AnnotationException, UIMAException, ClassNotFoundException, IOException { Integer address = aRequest.getParameterValue("id").toInteger(); String spanType = removePrefix(aRequest.getParameterValue("type").toString()); String username = aCurationUserSegment.getUsername(); SourceDocument sourceDocument = aCurationUserSegment.getBratAnnotatorModel().getDocument(); AnnotationDocument clickedAnnotationDocument = null; List<AnnotationDocument> annotationDocuments = documentService .listAnnotationDocuments(sourceDocument); for (AnnotationDocument annotationDocument : annotationDocuments) { if (annotationDocument.getUser().equals(username)) { clickedAnnotationDocument = annotationDocument; break; } } createSpan(spanType, aCurationUserSegment.getBratAnnotatorModel(), aJcas, clickedAnnotationDocument, address); } private void createSpan(String spanType, AnnotatorState aBModel, JCas aMergeJCas, AnnotationDocument aAnnotationDocument, int aAddress) throws IOException, UIMAException, ClassNotFoundException, AnnotationException { JCas clickedJCas = getJCas(aBModel, aAnnotationDocument); AnnotationFS fsClicked = selectByAddr(clickedJCas, aAddress); if(isCorefType(fsClicked)){ throw new AnnotationException(" Coreference Annotation not supported in curation"); } long layerId = TypeUtil.getLayerId(spanType); AnnotationLayer layer = annotationService.getLayer(layerId); MergeCas.addSpanAnnotation(annotationService, layer, aMergeJCas, fsClicked, layer.isAllowStacking()); writeEditorCas(aBModel, aMergeJCas); // update timestamp int sentenceNumber = getSentenceNumber(clickedJCas, fsClicked.getBegin()); aBModel.setFocusSentenceNumber(sentenceNumber); aBModel.getDocument().setSentenceAccessed(sentenceNumber); if (aBModel.getPreferences().isScrollPage()) { Sentence sentence = selectSentenceAt(aMergeJCas, aBModel.getFirstVisibleSentenceBegin(), aBModel.getFirstVisibleSentenceEnd()); sentence = findWindowStartCenteringOnSelection(aMergeJCas, sentence, fsClicked.getBegin(), aBModel.getProject(), aBModel.getDocument(), aBModel.getPreferences().getWindowSize()); aBModel.setFirstVisibleSentence(sentence); } } private void mergeArc(IRequestParameters aRequest, CurationUserSegmentForAnnotationDocument aCurationUserSegment, JCas aJcas) throws AnnotationException, IOException, UIMAException, ClassNotFoundException { Integer addressOriginClicked = aRequest.getParameterValue("originSpanId").toInteger(); Integer addressTargetClicked = aRequest.getParameterValue("targetSpanId").toInteger(); String arcType = removePrefix(aRequest.getParameterValue("type").toString()); String fsArcaddress = aRequest.getParameterValue("arcId").toString(); String username = aCurationUserSegment.getUsername(); AnnotatorState bModel = aCurationUserSegment.getBratAnnotatorModel(); SourceDocument sourceDocument = bModel.getDocument(); JCas clickedJCas = null; // for correction and automation, the lower panel is the clickedJcase, from the suggestions if (!aCurationUserSegment.getBratAnnotatorModel().getMode().equals(Mode.CURATION)) { clickedJCas = correctionDocumentService.readCorrectionCas(sourceDocument); } else{ AnnotationDocument clickedAnnotationDocument = documentService .listAnnotationDocuments(sourceDocument).stream() .filter(an -> an.getUser().equals(username)).findFirst().get(); try { clickedJCas = getJCas(bModel, clickedAnnotationDocument); } catch (IOException e1) { throw new IOException(); } } long layerId = TypeUtil.getLayerId(arcType); AnnotationLayer layer = annotationService.getLayer(layerId); TypeAdapter adapter = TypeUtil.getAdapter(annotationService, layer); int address = Integer.parseInt(fsArcaddress.split("\\.")[0]); AnnotationFS clickedFS = selectByAddr(clickedJCas, address); if(isCorefType(clickedFS)){ throw new AnnotationException(" Coreference Annotation not supported in curation"); } MergeCas.addArcAnnotation(adapter, aJcas, addressOriginClicked, addressTargetClicked, fsArcaddress, clickedJCas, clickedFS); writeEditorCas(bModel, aJcas); int sentenceNumber = getSentenceNumber(clickedJCas, clickedFS.getBegin()); bModel.setFocusSentenceNumber(sentenceNumber); // Update timestamp bModel.getDocument().setSentenceAccessed(sentenceNumber); if (bModel.getPreferences().isScrollPage()) { Sentence sentence = selectSentenceAt(aJcas, bModel.getFirstVisibleSentenceBegin(), bModel.getFirstVisibleSentenceEnd()); sentence = findWindowStartCenteringOnSelection(aJcas, sentence, clickedFS.getBegin(), bModel.getProject(), bModel.getDocument(), bModel.getPreferences().getWindowSize()); bModel.setFirstVisibleSentence(sentence); } } private JCas getJCas(AnnotatorState aState, AnnotationDocument aDocument) throws IOException { if (aState.getMode().equals(Mode.AUTOMATION) || aState.getMode().equals(Mode.CORRECTION)) { return correctionDocumentService.readCorrectionCas(aState.getDocument()); } else { return documentService.readAnnotationCas(aDocument); } } private void writeEditorCas(AnnotatorState aState, JCas aJCas) throws IOException { if (aState.getMode().equals(Mode.ANNOTATION) || aState.getMode().equals(Mode.AUTOMATION) || aState.getMode().equals(Mode.CORRECTION)) { documentService.writeAnnotationCas(aJCas, aState.getDocument(), aState.getUser(), true); } else if (aState.getMode().equals(Mode.CURATION)) { curationDocumentService.writeCurationCas(aJCas, aState.getDocument(), aState.getUser(), true); } } /** * Removes a prefix that is added to brat visualization for different color coded purpose. */ private static String removePrefix(String aType) { return aType.replace("_(" + AnnotationState.AGREE.name() + ")", "") .replace("_(" + AnnotationState.USE.name() + ")", "") .replace("_(" + AnnotationState.DISAGREE.name() + ")", "") .replace("_(" + AnnotationState.DO_NOT_USE.name() + ")", "") .replace("_(" + AnnotationState.NOT_SUPPORTED.name() + ")", ""); } public final static String CURATION_USER = "CURATION_USER"; private void populateCurationSentences( Map<String, JCas> aJCases, List<CurationUserSegmentForAnnotationDocument> aSentences, AnnotatorState aBratAnnotatorModel, final List<AnnotationOption> aAnnotationOptions, Map<String, Map<Integer, AnnotationSelection>> aAnnotationSelectionByUsernameAndAddress, AnnotationSchemaService aAnnotationService, CurationContainer aCurationContainer, final Map<String, AnnotationState> aStates) throws IOException { List<String> usernamesSorted = new ArrayList<String>(aJCases.keySet()); Collections.sort(usernamesSorted); final Mode mode = aBratAnnotatorModel.getMode(); boolean isAutomationMode = mode.equals(Mode.AUTOMATION); boolean isCorrectionMode = mode.equals(Mode.CORRECTION); boolean isCurationMode = mode.equals(Mode.CURATION); String annotatorCasUser; switch (mode) { case AUTOMATION: // fall-through case CORRECTION: annotatorCasUser = SecurityContextHolder.getContext().getAuthentication().getName(); break; case CURATION: annotatorCasUser = CURATION_USER; break; default: throw new IllegalStateException("Illegal mode [" + mode + "]"); } LOG.debug("mode = [" + mode + "]"); LOG.debug("all users is " + usernamesSorted); LOG.debug("annotator CAS is for user [" + annotatorCasUser + "]"); for (String username : usernamesSorted) { if ((!username.equals(CURATION_USER) && isCurationMode) || (username.equals(CURATION_USER) && (isAutomationMode || isCorrectionMode))) { JCas jCas = aJCases.get(username); // Set up coloring strategy ColoringStrategy curationColoringStrategy = new ColoringStrategy() { @Override public String getColor(Object aObj, String aLabel) { if (aStates.get(aObj.toString())==null){ return AnnotationState.NOT_SUPPORTED.getColorCode(); } return aStates.get(aObj.toString()).getColorCode(); } }; // Create curation view for the current user CurationUserSegmentForAnnotationDocument curationUserSegment2 = new CurationUserSegmentForAnnotationDocument(); curationUserSegment2.setCollectionData(getCollectionInformation(aAnnotationService, aCurationContainer)); curationUserSegment2.setDocumentResponse(render(jCas, aBratAnnotatorModel, curationColoringStrategy)); curationUserSegment2.setUsername(username); curationUserSegment2.setBratAnnotatorModel(aBratAnnotatorModel); curationUserSegment2 .setAnnotationSelectionByUsernameAndAddress(aAnnotationSelectionByUsernameAndAddress); aSentences.add(curationUserSegment2); } } } private String render(JCas aJcas, AnnotatorState aBratAnnotatorModel, ColoringStrategy aCurationColoringStrategy) throws IOException { AnnotationSchemaService aAnnotationService = annotationService; GetDocumentResponse response = new GetDocumentResponse(); response.setRtlMode(ScriptDirection.RTL.equals(aBratAnnotatorModel.getScriptDirection())); // Render invisible baseline annotations (sentence, tokens) BratRenderer.renderTokenAndSentence(aJcas, response, aBratAnnotatorModel); // Render visible (custom) layers for (AnnotationLayer layer : aBratAnnotatorModel.getAnnotationLayers()) { if (layer.getName().equals(Token.class.getName()) || layer.getName().equals(Sentence.class.getName()) || WebAnnoConst.CHAIN_TYPE.equals(layer.getType())) { continue; } List<AnnotationFeature> features = aAnnotationService.listAnnotationFeature(layer); List<AnnotationFeature> invisibleFeatures = new ArrayList<AnnotationFeature>(); for (AnnotationFeature feature : features) { if (!feature.isVisible()) { invisibleFeatures.add(feature); } } features.removeAll(invisibleFeatures); TypeAdapter adapter = getAdapter(aAnnotationService, layer); TypeRenderer renderer = BratRenderer.getRenderer(adapter); renderer.render(aJcas, features, response, aBratAnnotatorModel, aCurationColoringStrategy); } return JSONUtil.toInterpretableJsonString(response); } private String getCollectionInformation(AnnotationSchemaService aAnnotationService, CurationContainer aCurationContainer) throws IOException { GetCollectionInformationResponse info = new GetCollectionInformationResponse(); info.setEntityTypes(BratRenderer.buildEntityTypes(aCurationContainer .getBratAnnotatorModel().getAnnotationLayers(), aAnnotationService)); StringWriter out = new StringWriter(); JsonGenerator jsonGenerator = JSONUtil.getJsonConverter().getObjectMapper() .getFactory().createGenerator(out); jsonGenerator.writeObject(info); return out.toString(); } /** * @param aTarget * the AJAX target. * @param aCurationContainer * the container. * @param aMergeVisualizer * the annotator component. * @param aAnnotationSelectionByUsernameAndAddress * selections by user. * @param aCurationSegment * the segment. * @throws UIMAException * hum? * @throws ClassNotFoundException * hum? * @throws IOException * hum? * @throws AnnotationException * hum? */ public void updatePanel( AjaxRequestTarget aTarget, CurationContainer aCurationContainer, AnnotationEditorBase aMergeVisualizer, Map<String, Map<Integer, AnnotationSelection>> aAnnotationSelectionByUsernameAndAddress, SourceListView aCurationSegment) throws UIMAException, ClassNotFoundException, IOException, AnnotationException { AnnotatorState bModel = aCurationContainer.getBratAnnotatorModel(); SourceDocument sourceDocument = bModel.getDocument(); Map<String, JCas> jCases = new HashMap<String, JCas>(); // This is the CAS that the user can actively edit JCas annotatorCas = getAnnotatorCas(bModel, aAnnotationSelectionByUsernameAndAddress, sourceDocument, jCases); // We store the CAS that the user will edit as the "CURATION USER" jCases.put(CURATION_USER, annotatorCas); // get differing feature structures List<Type> entryTypes = SuggestionBuilder.getEntryTypes(annotatorCas, bModel.getAnnotationLayers(), annotationService); List<AnnotationOption> annotationOptions = null; Map<String, AnnotationState> annoStates = new HashMap<>(); DiffResult diff; if (bModel.getMode().equals(Mode.CURATION)) { diff = CasDiff2.doDiffSingle(annotationService, bModel.getProject(), entryTypes, LinkCompareBehavior.LINK_ROLE_AS_LABEL, jCases, aCurationSegment.getCurationBegin(), aCurationSegment.getCurationEnd()); } else { diff = CasDiff2.doDiffSingle(annotationService, bModel.getProject(), entryTypes, LinkCompareBehavior.LINK_ROLE_AS_LABEL, jCases, aCurationSegment.getBegin(), aCurationSegment.getEnd()); } Collection<ConfigurationSet> d = diff.getDifferingConfigurationSets().values(); Collection<ConfigurationSet> i = diff.getIncompleteConfigurationSets().values(); for (ConfigurationSet cfgSet : d) { if (i.contains(cfgSet)) { i.remove(cfgSet); } } addSuggestionColor(bModel.getMode(), jCases, annoStates, d, false, false); addSuggestionColor(bModel.getMode(), jCases, annoStates, i, true, false); List<ConfigurationSet> all = new ArrayList<>(); for (ConfigurationSet a : diff.getConfigurationSets()) { all.add(a); } for (ConfigurationSet cfgSet : d) { all.remove(cfgSet); } for (ConfigurationSet cfgSet : i) { all.remove(cfgSet); } addSuggestionColor(bModel.getMode(), jCases, annoStates, all, false, true); LinkedList<CurationUserSegmentForAnnotationDocument> sentences = new LinkedList<CurationUserSegmentForAnnotationDocument>(); populateCurationSentences(jCases, sentences, bModel, annotationOptions, aAnnotationSelectionByUsernameAndAddress, annotationService, aCurationContainer, annoStates); // update sentence list on the right side this.setModelObject(sentences); aTarget.add(this); } /** * For each {@link ConfigurationSet}, where there are some differences in users annotation and * the curation annotation. */ private void addSuggestionColor(Mode aMode, Map<String, JCas> aCasMap, Map<String, AnnotationState> aSuggestionColors, Collection<ConfigurationSet> aCfgSet, boolean aI, boolean aAgree) { for (ConfigurationSet cs : aCfgSet) { boolean use = false; for (String u : cs.getCasGroupIds()) { for (Configuration c : cs.getConfigurations(u)) { FeatureStructure fs = c.getFs(u, aCasMap); Object key = fs; // link FS if (c.getPosition().getFeature() != null) { ArrayFS links = (ArrayFS) fs.getFeatureValue(fs.getType() .getFeatureByBaseName(c.getPosition().getFeature())); FeatureStructure link = links.get(c.getAID(u).index); fs = (AnnotationFS) link.getFeatureValue(link.getType() .getFeatureByBaseName("target")); key = key + "-" + fs + "-" + link; } if (aAgree) { aSuggestionColors.put(key.toString(), AnnotationState.AGREE); continue; } // automation and correction projects if (!aMode.equals(Mode.CURATION) && !aAgree) { if (cs.getCasGroupIds().size() == 2) { aSuggestionColors.put(key.toString(), AnnotationState.DO_NOT_USE); } else { aSuggestionColors.put(key.toString(), AnnotationState.DISAGREE); } continue; } // this set agree with the curation annotation if (c.getCasGroupIds().contains(CURATION_USER)) { use = true; } else { use = false; } // this curation view if (u.equals(CURATION_USER)) { continue; } if (aAgree) { aSuggestionColors.put(key.toString(), AnnotationState.AGREE); } else if (use) { aSuggestionColors.put(key.toString(), AnnotationState.USE); } else if (aI) { aSuggestionColors.put(key.toString(), AnnotationState.DISAGREE); } else if (!cs.getCasGroupIds().contains(CURATION_USER)) { aSuggestionColors.put(key.toString(), AnnotationState.DISAGREE); } else { aSuggestionColors.put(key.toString(), AnnotationState.DO_NOT_USE); } } } } } private JCas getAnnotatorCas( AnnotatorState aBModel, Map<String, Map<Integer, AnnotationSelection>> aAnnotationSelectionByUsernameAndAddress, SourceDocument sourceDocument, Map<String, JCas> jCases) throws UIMAException, IOException, ClassNotFoundException { JCas annotatorCas; if (aBModel.getMode().equals(Mode.AUTOMATION) || aBModel.getMode().equals(Mode.CORRECTION)) { // If this is a CORRECTION or AUTOMATION project, then we get the CORRECTION document // and put it in as the single document to compare with. Basically what we do is that // we treat consider this scenario as a curation scenario where the CORRECTION document // is the only document we compare with. // The CAS the user can edit is the one from the virtual CORRECTION USER annotatorCas = correctionDocumentService.readCorrectionCas(sourceDocument); User user = userRepository.get(SecurityContextHolder.getContext().getAuthentication() .getName()); AnnotationDocument annotationDocument = documentService.getAnnotationDocument( sourceDocument, user); jCases.put(user.getUsername(), documentService.readAnnotationCas(annotationDocument)); aAnnotationSelectionByUsernameAndAddress.put(CURATION_USER, new HashMap<Integer, AnnotationSelection>()); } else { // If this is a true CURATION then we get all the annotation documents from all the // active users. // The CAS the user can edit is the one from the virtual CURATION USER annotatorCas = curationDocumentService.readCurationCas(sourceDocument); // Now we get all the other CASes from the repository List<AnnotationDocument> annotationDocuments = documentService .listAnnotationDocuments(sourceDocument); for (AnnotationDocument annotationDocument : annotationDocuments) { String username = annotationDocument.getUser(); if (annotationDocument.getState().equals(AnnotationDocumentState.FINISHED) || username.equals(CURATION_USER)) { JCas jCas = documentService.readAnnotationCas(annotationDocument); jCases.put(username, jCas); // cleanup annotationSelections aAnnotationSelectionByUsernameAndAddress.put(username, new HashMap<Integer, AnnotationSelection>()); } } } return annotatorCas; } }