/* * Copyright 2015 * 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.util; import static de.tudarmstadt.ukp.clarin.webanno.api.WebAnnoConst.SPAN_TYPE; import static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.apache.uima.cas.CAS; 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.fit.factory.JCasFactory; import org.apache.uima.fit.util.CasUtil; import org.apache.uima.jcas.JCas; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import de.tudarmstadt.ukp.clarin.webanno.api.AnnotationSchemaService; import de.tudarmstadt.ukp.clarin.webanno.api.WebAnnoConst; import de.tudarmstadt.ukp.clarin.webanno.api.annotation.exception.AnnotationException; import de.tudarmstadt.ukp.clarin.webanno.api.annotation.util.WebAnnoCasUtil; import de.tudarmstadt.ukp.clarin.webanno.curation.casdiff.CasDiff2; import de.tudarmstadt.ukp.clarin.webanno.curation.casdiff.DiffUtils; import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationFeature; import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationLayer; import de.tudarmstadt.ukp.clarin.webanno.model.LinkMode; import de.tudarmstadt.ukp.clarin.webanno.model.Project; import de.tudarmstadt.ukp.clarin.webanno.ui.curation.util.MergeCas; import de.tudarmstadt.ukp.dkpro.core.api.lexmorph.type.pos.POS; import de.tudarmstadt.ukp.dkpro.core.api.segmentation.type.Token; import de.tudarmstadt.ukp.dkpro.core.api.syntax.type.dependency.Dependency; import mockit.Mock; import mockit.MockUp; public class CopyAnnotationTest { private AnnotationSchemaService annotationSchemaService; private Project project; private AnnotationLayer tokenLayer; private AnnotationFeature tokenPosFeature; private AnnotationLayer posLayer; private AnnotationFeature posFeature; private AnnotationLayer slotLayer; private AnnotationFeature slotFeature; private AnnotationFeature stringFeature; @Before public void setup() { project = new Project(); tokenLayer = new AnnotationLayer(Token.class.getName(), "Token", SPAN_TYPE, null, true); tokenPosFeature = new AnnotationFeature(); tokenPosFeature.setName("pos"); tokenPosFeature.setEnabled(true); tokenPosFeature.setType(POS.class.getName()); tokenPosFeature.setUiName("pos"); tokenPosFeature.setLayer(tokenLayer); tokenPosFeature.setProject(project); tokenPosFeature.setVisible(true); posLayer = new AnnotationLayer(POS.class.getName(), "POS", SPAN_TYPE, project, true); posLayer.setAttachType(tokenLayer); posLayer.setAttachFeature(tokenPosFeature); posFeature = new AnnotationFeature(); posFeature.setName("PosValue"); posFeature.setEnabled(true); posFeature.setType(CAS.TYPE_NAME_STRING); posFeature.setUiName("PosValue"); posFeature.setLayer(posLayer); posFeature.setProject(project); posFeature.setVisible(true); slotLayer = new AnnotationLayer(DiffUtils.HOST_TYPE, DiffUtils.HOST_TYPE, SPAN_TYPE, project, false); slotFeature = new AnnotationFeature(); slotFeature.setName("links"); slotFeature.setEnabled(true); slotFeature.setType(Token.class.getName()); slotFeature.setLinkMode(LinkMode.WITH_ROLE); slotFeature.setUiName("f1"); slotFeature.setLayer(slotLayer); slotFeature.setProject(project); slotFeature.setVisible(true); stringFeature = new AnnotationFeature(); stringFeature.setName("f1"); stringFeature.setEnabled(true); stringFeature.setType(CAS.TYPE_NAME_STRING); stringFeature.setUiName("f1"); stringFeature.setLayer(slotLayer); stringFeature.setProject(project); stringFeature.setVisible(true); annotationSchemaService = new MockUp<AnnotationSchemaService>() { @Mock List<AnnotationFeature> listAnnotationFeature(AnnotationLayer type) { if (type.getName().equals(POS.class.getName())) { return asList(posFeature); } if (type.getName().equals(DiffUtils.HOST_TYPE)) { return asList(slotFeature, stringFeature); } throw new IllegalStateException("Unknown layer type: " + type.getName()); } }.getMockInstance(); } @Test public void simpleCopyToEmptyTest() throws Exception { JCas jcas = JCasFactory.createJCas(); Type type = jcas.getTypeSystem().getType(POS.class.getTypeName()); AnnotationFS clickedFs = createPOSAnno(jcas, type, "NN", 0, 0); JCas mergeCAs = JCasFactory.createJCas(); createTokenAnno(mergeCAs, 0, 0); MergeCas.addSpanAnnotation(annotationSchemaService, posLayer, mergeCAs, clickedFs, false); assertEquals(1, CasUtil.selectCovered(mergeCAs.getCas(), type, 0, 0).size()); } private AnnotationFS createPOSAnno(JCas aJCas, Type aType, String aValue, int aBegin, int aEnd) { AnnotationFS clickedFs = aJCas.getCas().createAnnotation(aType, aBegin, aEnd); Feature posValue = aType.getFeatureByBaseName("PosValue"); clickedFs.setStringValue(posValue, aValue); aJCas.addFsToIndexes(clickedFs); return clickedFs; } private AnnotationFS createTokenAnno(JCas aJCas, int aBegin, int aEnd) { Type type = aJCas.getTypeSystem().getType(Token.class.getTypeName()); AnnotationFS token = aJCas.getCas().createAnnotation(type, aBegin, aEnd); aJCas.addFsToIndexes(token); return token; } @Test public void simpleCopyToSameExistingAnnoTest() throws Exception { JCas jcas = JCasFactory.createJCas(); Type type = jcas.getTypeSystem().getType(POS.class.getTypeName()); AnnotationFS clickedFs = createPOSAnno(jcas, type, "NN", 0, 0); JCas mergeCAs = JCasFactory.createJCas(); AnnotationFS existingFs = mergeCAs.getCas().createAnnotation(type, 0, 0); Feature posValue = type.getFeatureByBaseName("PosValue"); existingFs.setStringValue(posValue, "NN"); mergeCAs.addFsToIndexes(existingFs); exception.expect(AnnotationException.class); MergeCas.addSpanAnnotation(annotationSchemaService, posLayer, mergeCAs, clickedFs, false); } @Test public void simpleCopyToDiffExistingAnnoWithNoStackingTest() throws Exception { JCas jcas = JCasFactory.createJCas(); Type type = jcas.getTypeSystem().getType(POS.class.getTypeName()); AnnotationFS clickedFs = createPOSAnno(jcas, type, "NN", 0, 0); JCas mergeCAs = JCasFactory.createJCas(); AnnotationFS existingFs = mergeCAs.getCas().createAnnotation(type, 0, 0); Feature posValue = type.getFeatureByBaseName("PosValue"); existingFs.setStringValue(posValue, "NE"); mergeCAs.addFsToIndexes(existingFs); MergeCas.addSpanAnnotation(annotationSchemaService, posLayer, mergeCAs, clickedFs, false); assertEquals(1, CasUtil.selectCovered(mergeCAs.getCas(), type, 0, 0).size()); } @Test public void simpleCopyToDiffExistingAnnoWithStackingTest() throws Exception { posLayer.setAllowStacking(true); JCas jcas = JCasFactory.createJCas(); Type type = jcas.getTypeSystem().getType(POS.class.getTypeName()); AnnotationFS clickedFs = createPOSAnno(jcas, type, "NN", 0, 0); JCas mergeCAs = JCasFactory.createJCas(); createTokenAnno(mergeCAs, 0, 0); AnnotationFS existingFs = mergeCAs.getCas().createAnnotation(type, 0, 0); Feature posValue = type.getFeatureByBaseName("PosValue"); existingFs.setStringValue(posValue, "NE"); mergeCAs.addFsToIndexes(existingFs); MergeCas.addSpanAnnotation(annotationSchemaService, posLayer, mergeCAs, clickedFs, true); assertEquals(2, CasUtil.selectCovered(mergeCAs.getCas(), type, 0, 0).size()); } @Test public void copySpanWithSlotNoStackingTest() throws Exception { slotLayer.setAllowStacking(false); JCas jcasA = JCasFactory.createJCas(DiffUtils.createMultiLinkWithRoleTestTypeSytem("f1")); Type type = jcasA.getTypeSystem().getType(DiffUtils.HOST_TYPE); Feature feature = type.getFeatureByBaseName("f1"); AnnotationFS clickedFs = DiffUtils.makeLinkHostMultiSPanFeatureFS(jcasA, 0, 0, feature, "A", DiffUtils.makeLinkFS(jcasA, "slot1", 0, 0)); JCas mergeCAs = JCasFactory .createJCas(DiffUtils.createMultiLinkWithRoleTestTypeSytem("f1")); DiffUtils.makeLinkHostMultiSPanFeatureFS(mergeCAs, 0, 0, feature, "C", DiffUtils.makeLinkFS(mergeCAs, "slot1", 0, 0)); MergeCas.addSpanAnnotation(annotationSchemaService, slotLayer, mergeCAs, clickedFs, false); assertEquals(1, CasUtil.selectCovered(mergeCAs.getCas(), type, 0, 0).size()); } @Test public void copySpanWithSlotWithStackingTest() throws Exception { slotLayer.setAllowStacking(true); JCas jcasA = JCasFactory.createJCas(DiffUtils.createMultiLinkWithRoleTestTypeSytem("f1")); Type type = jcasA.getTypeSystem().getType(DiffUtils.HOST_TYPE); Feature feature = type.getFeatureByBaseName("f1"); AnnotationFS clickedFs = DiffUtils.makeLinkHostMultiSPanFeatureFS(jcasA, 0, 0, feature, "A", DiffUtils.makeLinkFS(jcasA, "slot1", 0, 0)); JCas mergeCAs = JCasFactory .createJCas(DiffUtils.createMultiLinkWithRoleTestTypeSytem("f1")); DiffUtils.makeLinkHostMultiSPanFeatureFS(mergeCAs, 0, 0, feature, "C", DiffUtils.makeLinkFS(mergeCAs, "slot1", 0, 0)); MergeCas.addSpanAnnotation(annotationSchemaService, slotLayer, mergeCAs, clickedFs, true); assertEquals(2, CasUtil.selectCovered(mergeCAs.getCas(), type, 0, 0).size()); } @Test public void copyLinkToEmptyTest() throws Exception { JCas mergeCAs = JCasFactory .createJCas(DiffUtils.createMultiLinkWithRoleTestTypeSytem("f1")); Type type = mergeCAs.getTypeSystem().getType(DiffUtils.HOST_TYPE); Feature feature = type.getFeatureByBaseName("f1"); AnnotationFS mergeFs = DiffUtils.makeLinkHostMultiSPanFeatureFS(mergeCAs, 0, 0, feature, "A"); FeatureStructure copyFS = DiffUtils.makeLinkFS(mergeCAs, "slot1", 0, 0); List<FeatureStructure> linkFs = new ArrayList<>(); linkFs.add(copyFS); WebAnnoCasUtil.setLinkFeatureValue(mergeFs, type.getFeatureByBaseName("links"), linkFs); JCas jcasA = JCasFactory.createJCas(DiffUtils.createMultiLinkWithRoleTestTypeSytem("f1")); DiffUtils.makeLinkHostMultiSPanFeatureFS(jcasA, 0, 0, feature, "A", DiffUtils.makeLinkFS(jcasA, "slot1", 0, 0)); Map<String, List<JCas>> casByUser = new LinkedHashMap<>(); casByUser.put("user1", asList(mergeCAs)); casByUser.put("user2", asList(jcasA)); List<String> entryTypes = asList(DiffUtils.HOST_TYPE); CasDiff2.SpanDiffAdapter adapter = new CasDiff2.SpanDiffAdapter(DiffUtils.HOST_TYPE); adapter.addLinkFeature("links", "role", "target"); List<? extends CasDiff2.DiffAdapter> diffAdapters = asList(adapter); CasDiff2.DiffResult diff = CasDiff2.doDiff(entryTypes, diffAdapters, CasDiff2.LinkCompareBehavior.LINK_TARGET_AS_LABEL, casByUser); assertEquals(0, diff.getDifferingConfigurationSets().size()); assertEquals(0, diff.getIncompleteConfigurationSets().size()); } @Test public void copyLinkToExistingButDiffLinkTest() throws Exception { JCas mergeCAs = JCasFactory .createJCas(DiffUtils.createMultiLinkWithRoleTestTypeSytem("f1")); Type type = mergeCAs.getTypeSystem().getType(DiffUtils.HOST_TYPE); Feature feature = type.getFeatureByBaseName("f1"); AnnotationFS mergeFs = DiffUtils.makeLinkHostMultiSPanFeatureFS(mergeCAs, 0, 0, feature, "A", DiffUtils.makeLinkFS(mergeCAs, "slot1", 0, 0)); FeatureStructure copyFS = DiffUtils.makeLinkFS(mergeCAs, "slot2", 0, 0); List<FeatureStructure> linkFs = new ArrayList<>(); linkFs.add(copyFS); WebAnnoCasUtil.setLinkFeatureValue(mergeFs, type.getFeatureByBaseName("links"), linkFs); JCas jcasA = JCasFactory.createJCas(DiffUtils.createMultiLinkWithRoleTestTypeSytem("f1")); DiffUtils.makeLinkHostMultiSPanFeatureFS(jcasA, 0, 0, feature, "A", DiffUtils.makeLinkFS(jcasA, "slot1", 0, 0)); Map<String, List<JCas>> casByUser = new LinkedHashMap<>(); casByUser.put("user1", asList(mergeCAs)); casByUser.put("user2", asList(jcasA)); List<String> entryTypes = asList(DiffUtils.HOST_TYPE); CasDiff2.SpanDiffAdapter adapter = new CasDiff2.SpanDiffAdapter(DiffUtils.HOST_TYPE); adapter.addLinkFeature("links", "role", "target"); List<? extends CasDiff2.DiffAdapter> diffAdapters = asList(adapter); CasDiff2.DiffResult diff = CasDiff2.doDiff(entryTypes, diffAdapters, CasDiff2.LinkCompareBehavior.LINK_TARGET_AS_LABEL, casByUser); assertEquals(0, diff.getDifferingConfigurationSets().size()); assertEquals(2, diff.getIncompleteConfigurationSets().size()); } @Test public void simpleCopyRelationToEmptyAnnoTest() throws Exception { JCas jcas = JCasFactory.createJCas(); Type type = jcas.getTypeSystem().getType(Dependency.class.getTypeName()); Type posType = jcas.getTypeSystem().getType(POS.class.getTypeName()); AnnotationFS originClickedToken = createTokenAnno(jcas, 0, 0); AnnotationFS targetClickedToken = createTokenAnno(jcas, 1, 1); AnnotationFS originClicked = createPOSAnno(jcas, posType, "NN", 0, 0); AnnotationFS targetClicked = createPOSAnno(jcas, posType, "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(WebAnnoConst.FEAT_REL_SOURCE); Feature targetFeature = type.getFeatureByBaseName(WebAnnoConst.FEAT_REL_TARGET); AnnotationFS clickedFs = jcas.getCas().createAnnotation(type, 0, 1); clickedFs.setFeatureValue(sourceFeature, originClickedToken); clickedFs.setFeatureValue(targetFeature, targetClickedToken); jcas.addFsToIndexes(clickedFs); JCas mergeCAs = JCasFactory.createJCas(); AnnotationFS origin = createPOSAnno(mergeCAs, posType, "NN", 0, 0); AnnotationFS target = createPOSAnno(mergeCAs, posType, "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); MergeCas.addRelationArcAnnotation(mergeCAs, clickedFs, true, false, originToken, targetToken); assertEquals(1, CasUtil.selectCovered(mergeCAs.getCas(), type, 0, 1).size()); } @Test public void simpleCopyRelationToStackedTargetsTest() throws Exception { JCas jcas = JCasFactory.createJCas(); Type type = jcas.getTypeSystem().getType(Dependency.class.getTypeName()); Type posType = jcas.getTypeSystem().getType(POS.class.getTypeName()); AnnotationFS originClickedToken = createTokenAnno(jcas, 0, 0); AnnotationFS targetClickedToken = createTokenAnno(jcas, 1, 1); AnnotationFS originClicked = createPOSAnno(jcas, posType, "NN", 0, 0); AnnotationFS targetClicked = createPOSAnno(jcas, posType, "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(WebAnnoConst.FEAT_REL_SOURCE); Feature targetFeature = type.getFeatureByBaseName(WebAnnoConst.FEAT_REL_TARGET); AnnotationFS clickedFs = jcas.getCas().createAnnotation(type, 0, 1); clickedFs.setFeatureValue(sourceFeature, originClickedToken); clickedFs.setFeatureValue(targetFeature, targetClickedToken); jcas.addFsToIndexes(clickedFs); JCas mergeCAs = JCasFactory.createJCas(); AnnotationFS origin = createPOSAnno(mergeCAs, posType, "NN", 0, 0); AnnotationFS target = createPOSAnno(mergeCAs, posType, "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, posType, "NN", 0, 0); AnnotationFS target2 = createPOSAnno(mergeCAs, posType, "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); exception.expect(AnnotationException.class); MergeCas.addRelationArcAnnotation(mergeCAs, clickedFs, true, false, originToken, targetToken); } @Test public void simpleCopyStackedRelationTest() throws Exception { JCas jcas = JCasFactory.createJCas(); Type type = jcas.getTypeSystem().getType(Dependency.class.getTypeName()); Type posType = jcas.getTypeSystem().getType(POS.class.getTypeName()); AnnotationFS originClickedToken = createTokenAnno(jcas, 0, 0); AnnotationFS targetClickedToken = createTokenAnno(jcas, 1, 1); AnnotationFS originClicked = createPOSAnno(jcas, posType, "NN", 0, 0); AnnotationFS targetClicked = createPOSAnno(jcas, posType, "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(WebAnnoConst.FEAT_REL_SOURCE); Feature targetFeature = type.getFeatureByBaseName(WebAnnoConst.FEAT_REL_TARGET); AnnotationFS clickedFs = jcas.getCas().createAnnotation(type, 0, 1); clickedFs.setFeatureValue(sourceFeature, originClickedToken); clickedFs.setFeatureValue(targetFeature, targetClickedToken); jcas.addFsToIndexes(clickedFs); JCas mergeCAs = JCasFactory.createJCas(); AnnotationFS origin = createPOSAnno(mergeCAs, posType, "NN", 0, 0); AnnotationFS target = createPOSAnno(mergeCAs, posType, "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.getCas().createAnnotation(type, 0, 1); existing.setFeatureValue(sourceFeature, originToken); existing.setFeatureValue(targetFeature, targetToken); mergeCAs.addFsToIndexes(clickedFs); exception.expect(AnnotationException.class); MergeCas.addRelationArcAnnotation(mergeCAs, clickedFs, true, false, originToken, targetToken); } @Rule public final ExpectedException exception = ExpectedException.none(); }