CasMerge { public CasMergeOperationResult mergeRelationAnnotation(SourceDocument aDocument, String aUsername, AnnotationLayer aAnnotationLayer, CAS aTargetCas, AnnotationFS aSourceFs, boolean aAllowStacking) throws AnnotationException { RelationAdapter adapter = (RelationAdapter) adapterCache.get(aAnnotationLayer); if (silenceEvents) { adapter.silenceEvents(); } AnnotationFS originFsClicked = FSUtil.getFeature(aSourceFs, adapter.getSourceFeatureName(), AnnotationFS.class); AnnotationFS targetFsClicked = FSUtil.getFeature(aSourceFs, adapter.getTargetFeatureName(), AnnotationFS.class); List<AnnotationFS> candidateRelations = getCandidateAnnotations(aTargetCas, aSourceFs); List<AnnotationFS> candidateOrigins = getCandidateAnnotations(aTargetCas, originFsClicked); List<AnnotationFS> candidateTargets = getCandidateAnnotations(aTargetCas, targetFsClicked); if (candidateOrigins.isEmpty() || candidateTargets.isEmpty()) { throw new UnfulfilledPrerequisitesException("Both the source and target annotation" + " must exist in the target document. Please first merge/create them"); } if (candidateOrigins.size() > 1) { throw new MergeConflictException( "Stacked sources exist in the target document. Cannot merge this relation."); } if (candidateTargets.size() > 1) { throw new MergeConflictException( "Stacked targets exist in the target document. Cannot merge this relation."); } if (!candidateRelations.isEmpty()) { throw new AlreadyMergedException( "The annotation already exists in the target document."); } AnnotationFS originFs = candidateOrigins.get(0); AnnotationFS targetFs = candidateTargets.get(0); if (adapter.getAttachFeatureName() != null) { AnnotationFS originAttachAnnotation = FSUtil.getFeature(originFs, adapter.getAttachFeatureName(), AnnotationFS.class); AnnotationFS targetAttachAnnotation = FSUtil.getFeature(targetFs, adapter.getAttachFeatureName(), AnnotationFS.class); if (originAttachAnnotation == null || targetAttachAnnotation == null) { throw new UnfulfilledPrerequisitesException( "No annotation to attach to. Cannot merge this relation."); } } List<AnnotationFS> existingAnnos = selectCandidateRelationsAt(aTargetCas, aSourceFs, originFs, targetFs); if (existingAnnos.isEmpty() || aAllowStacking) { AnnotationFS mergedRelation = adapter.add(aDocument, aUsername, originFs, targetFs, aTargetCas); try { copyFeatures(aDocument, aUsername, adapter, mergedRelation, aSourceFs); } catch (AnnotationException e) { adapter.delete(aDocument, aUsername, aTargetCas, new VID(mergedRelation)); } return new CasMergeOperationResult(CasMergeOperationResult.ResultState.CREATED, getAddr(mergedRelation)); } else { AnnotationFS mergeTargetFS = existingAnnos.get(0); copyFeatures(aDocument, aUsername, adapter, mergeTargetFS, aSourceFs); return new CasMergeOperationResult(CasMergeOperationResult.ResultState.UPDATED, getAddr(mergeTargetFS)); } } CasMerge(AnnotationSchemaService aSchemaService);  CasMerge(AnnotationSchemaService aSchemaService,
            ApplicationEventPublisher aEventPublisher); void setSilenceEvents(boolean aSilenceEvents); boolean isSilenceEvents(); void setMergeIncompleteAnnotations(boolean aMergeIncompleteAnnotations); boolean isMergeIncompleteAnnotations(); void reMergeCas(DiffResult aDiff, SourceDocument aTargetDocument, String aTargetUsername,
            CAS aTargetCas, Map<String, CAS> aCases); CasMergeOperationResult mergeSpanAnnotation(SourceDocument aDocument, String aUsername,
            AnnotationLayer aAnnotationLayer, CAS aTargetCas, AnnotationFS aSourceFs,
            boolean aAllowStacking); CasMergeOperationResult mergeRelationAnnotation(SourceDocument aDocument,
            String aUsername, AnnotationLayer aAnnotationLayer, CAS aTargetCas,
            AnnotationFS aSourceFs, boolean aAllowStacking); CasMergeOperationResult mergeSlotFeature(SourceDocument aDocument, String aUsername,
            AnnotationLayer aAnnotationLayer, CAS aTargetCas,
            AnnotationFS aSourceFs, String aSourceFeature, int aSourceSlotIndex);  }
@Test public void simpleCopyRelationToEmptyAnnoTest() throws Exception { CAS jcas = createJCas().getCas(); Type type = jcas.getTypeSystem().getType(Dependency.class.getTypeName()); AnnotationFS originClickedToken = createTokenAnno(jcas, 0, 0); AnnotationFS targetClickedToken = createTokenAnno(jcas, 1, 1); AnnotationFS originClicked = createPOSAnno(jcas, "NN", 0, 0); AnnotationFS targetClicked = createPOSAnno(jcas, "NN", 1, 1); jcas.addFsToIndexes(originClicked); jcas.addFsToIndexes(targetClicked); originClickedToken.setFeatureValue(originClickedToken.getType().getFeatureByBaseName("pos"), originClicked); targetClickedToken.setFeatureValue(targetClickedToken.getType().getFeatureByBaseName("pos"), targetClicked); Feature sourceFeature = type.getFeatureByBaseName(FEAT_REL_SOURCE); Feature targetFeature = type.getFeatureByBaseName(FEAT_REL_TARGET); AnnotationFS clickedFs = jcas.createAnnotation(type, 0, 1); clickedFs.setFeatureValue(sourceFeature, originClickedToken); clickedFs.setFeatureValue(targetFeature, targetClickedToken); jcas.addFsToIndexes(clickedFs); CAS mergeCAs = createJCas().getCas(); AnnotationFS origin = createPOSAnno(mergeCAs, "NN", 0, 0); AnnotationFS target = createPOSAnno(mergeCAs, "NN", 1, 1); mergeCAs.addFsToIndexes(origin); mergeCAs.addFsToIndexes(target); AnnotationFS originToken = createTokenAnno(mergeCAs, 0, 0); AnnotationFS targetToken = createTokenAnno(mergeCAs, 1, 1); originToken.setFeatureValue(originToken.getType().getFeatureByBaseName("pos"), origin); targetToken.setFeatureValue(targetToken.getType().getFeatureByBaseName("pos"), target); mergeCAs.addFsToIndexes(originToken); mergeCAs.addFsToIndexes(targetToken); sut.mergeRelationAnnotation(null, null, depLayer, mergeCAs, clickedFs, false); assertEquals(1, selectCovered(mergeCAs, type, 0, 1).size()); }
@Test public void simpleCopyRelationToStackedTargetsTest() throws Exception { CAS jcas = createJCas().getCas(); Type type = jcas.getTypeSystem().getType(Dependency.class.getTypeName()); AnnotationFS originClickedToken = createTokenAnno(jcas, 0, 0); AnnotationFS targetClickedToken = createTokenAnno(jcas, 1, 1); AnnotationFS originClicked = createPOSAnno(jcas, "NN", 0, 0); AnnotationFS targetClicked = createPOSAnno(jcas, "NN", 1, 1); jcas.addFsToIndexes(originClicked); jcas.addFsToIndexes(targetClicked); originClickedToken.setFeatureValue(originClickedToken.getType().getFeatureByBaseName("pos"), originClicked); targetClickedToken.setFeatureValue(targetClickedToken.getType().getFeatureByBaseName("pos"), targetClicked); Feature sourceFeature = type.getFeatureByBaseName(FEAT_REL_SOURCE); Feature targetFeature = type.getFeatureByBaseName(FEAT_REL_TARGET); AnnotationFS clickedFs = jcas.createAnnotation(type, 0, 1); clickedFs.setFeatureValue(sourceFeature, originClickedToken); clickedFs.setFeatureValue(targetFeature, targetClickedToken); jcas.addFsToIndexes(clickedFs); CAS mergeCAs = createJCas().getCas(); AnnotationFS origin = createPOSAnno(mergeCAs, "NN", 0, 0); AnnotationFS target = createPOSAnno(mergeCAs, "NN", 1, 1); mergeCAs.addFsToIndexes(origin); mergeCAs.addFsToIndexes(target); AnnotationFS originToken = createTokenAnno(mergeCAs, 0, 0); AnnotationFS targetToken = createTokenAnno(mergeCAs, 1, 1); originToken.setFeatureValue(originToken.getType().getFeatureByBaseName("pos"), origin); targetToken.setFeatureValue(targetToken.getType().getFeatureByBaseName("pos"), target); mergeCAs.addFsToIndexes(originToken); mergeCAs.addFsToIndexes(targetToken); AnnotationFS origin2 = createPOSAnno(mergeCAs, "NN", 0, 0); AnnotationFS target2 = createPOSAnno(mergeCAs, "NN", 1, 1); mergeCAs.addFsToIndexes(origin2); mergeCAs.addFsToIndexes(target2); AnnotationFS originToken2 = createTokenAnno(mergeCAs, 0, 0); AnnotationFS targetToken2 = createTokenAnno(mergeCAs, 1, 1); originToken2.setFeatureValue(originToken.getType().getFeatureByBaseName("pos"), origin2); targetToken2.setFeatureValue(targetToken.getType().getFeatureByBaseName("pos"), target2); mergeCAs.addFsToIndexes(originToken2); mergeCAs.addFsToIndexes(targetToken2); assertThatExceptionOfType(AnnotationException.class) .isThrownBy(() -> sut.mergeRelationAnnotation(null, null, depLayer, mergeCAs, clickedFs, false)) .withMessageContaining("Stacked sources exist"); }
@Test public void thatMergingRelationIsRejectedIfAlreadyExists() throws Exception { CAS jcas = createJCas().getCas(); Type type = jcas.getTypeSystem().getType(Dependency.class.getTypeName()); AnnotationFS originClickedToken = createTokenAnno(jcas, 0, 0); AnnotationFS targetClickedToken = createTokenAnno(jcas, 1, 1); AnnotationFS originClicked = createPOSAnno(jcas, "NN", 0, 0); AnnotationFS targetClicked = createPOSAnno(jcas, "NN", 1, 1); jcas.addFsToIndexes(originClicked); jcas.addFsToIndexes(targetClicked); originClickedToken.setFeatureValue(originClickedToken.getType().getFeatureByBaseName("pos"), originClicked); targetClickedToken.setFeatureValue(targetClickedToken.getType().getFeatureByBaseName("pos"), targetClicked); Feature sourceFeature = type.getFeatureByBaseName(FEAT_REL_SOURCE); Feature targetFeature = type.getFeatureByBaseName(FEAT_REL_TARGET); AnnotationFS clickedFs = jcas.createAnnotation(type, 0, 1); clickedFs.setFeatureValue(sourceFeature, originClickedToken); clickedFs.setFeatureValue(targetFeature, targetClickedToken); jcas.addFsToIndexes(clickedFs); CAS mergeCAs = createJCas().getCas(); AnnotationFS origin = createPOSAnno(mergeCAs, "NN", 0, 0); AnnotationFS target = createPOSAnno(mergeCAs, "NN", 1, 1); mergeCAs.addFsToIndexes(origin); mergeCAs.addFsToIndexes(target); AnnotationFS originToken = createTokenAnno(mergeCAs, 0, 0); AnnotationFS targetToken = createTokenAnno(mergeCAs, 1, 1); originToken.setFeatureValue(originToken.getType().getFeatureByBaseName("pos"), origin); targetToken.setFeatureValue(targetToken.getType().getFeatureByBaseName("pos"), target); mergeCAs.addFsToIndexes(originToken); mergeCAs.addFsToIndexes(targetToken); AnnotationFS existing = mergeCAs.createAnnotation(type, 0, 1); existing.setFeatureValue(sourceFeature, originToken); existing.setFeatureValue(targetFeature, targetToken); mergeCAs.addFsToIndexes(existing); assertThatExceptionOfType(AnnotationException.class) .isThrownBy(() -> sut.mergeRelationAnnotation(null, null, depLayer, mergeCAs, clickedFs, false)) .withMessageContaining("annotation already exists"); }