use of de.tudarmstadt.ukp.clarin.webanno.api.annotation.adapter.RelationAdapter in project webanno by webanno.
the class AnnotationSchemaServiceImpl method getAttachedRels.
@Override
@Transactional
public List<AttachedAnnotation> getAttachedRels(AnnotationLayer aLayer, AnnotationFS aFs) {
CAS cas = aFs.getCAS();
List<AttachedAnnotation> result = new ArrayList<>();
for (AnnotationLayer relationLayer : listAttachedRelationLayers(aLayer)) {
RelationAdapter relationAdapter = (RelationAdapter) getAdapter(relationLayer);
Type relationType = CasUtil.getType(cas, relationLayer.getName());
Feature sourceFeature = relationType.getFeatureByBaseName(relationAdapter.getSourceFeatureName());
Feature targetFeature = relationType.getFeatureByBaseName(relationAdapter.getTargetFeatureName());
// This code is already prepared for the day that relations can go between
// different layers and may have different attach features for the source and
// target layers.
Feature relationSourceAttachFeature = null;
Feature relationTargetAttachFeature = null;
if (relationAdapter.getAttachFeatureName() != null) {
relationSourceAttachFeature = sourceFeature.getRange().getFeatureByBaseName(relationAdapter.getAttachFeatureName());
relationTargetAttachFeature = targetFeature.getRange().getFeatureByBaseName(relationAdapter.getAttachFeatureName());
}
for (AnnotationFS relationFS : CasUtil.select(cas, relationType)) {
if (!(relationFS instanceof AnnotationFS)) {
continue;
}
// Here we get the annotations that the relation is pointing to in the UI
AnnotationFS sourceFS;
if (relationSourceAttachFeature != null) {
sourceFS = (AnnotationFS) relationFS.getFeatureValue(sourceFeature).getFeatureValue(relationSourceAttachFeature);
} else {
sourceFS = (AnnotationFS) relationFS.getFeatureValue(sourceFeature);
}
AnnotationFS targetFS;
if (relationTargetAttachFeature != null) {
targetFS = (AnnotationFS) relationFS.getFeatureValue(targetFeature).getFeatureValue(relationTargetAttachFeature);
} else {
targetFS = (AnnotationFS) relationFS.getFeatureValue(targetFeature);
}
if (sourceFS == null || targetFS == null) {
StringBuilder message = new StringBuilder();
message.append("Relation [" + relationAdapter.getLayer().getName() + "] with id [" + getAddr(relationFS) + "] has loose ends - cannot identify attached annotations.");
if (relationAdapter.getAttachFeatureName() != null) {
message.append("\nRelation [" + relationAdapter.getLayer().getName() + "] attached to feature [" + relationAdapter.getAttachFeatureName() + "].");
}
message.append("\nSource: " + sourceFS);
message.append("\nTarget: " + targetFS);
log.warn("{}", message.toString());
continue;
}
boolean isIncoming = isSame(targetFS, aFs);
boolean isOutgoing = isSame(sourceFS, aFs);
if (isIncoming && isOutgoing) {
result.add(new AttachedAnnotation(relationLayer, relationFS, sourceFS, LOOP));
} else if (isIncoming) {
result.add(new AttachedAnnotation(relationLayer, relationFS, sourceFS, INCOMING));
} else if (isOutgoing) {
result.add(new AttachedAnnotation(relationLayer, relationFS, targetFS, OUTGOING));
}
}
}
return result;
}
use of de.tudarmstadt.ukp.clarin.webanno.api.annotation.adapter.RelationAdapter in project webanno by webanno.
the class RelationRendererTest method thatRelationOverlapBehaviorOnRenderGeneratesErrors.
@Test
public void thatRelationOverlapBehaviorOnRenderGeneratesErrors() throws Exception {
TokenBuilder<Token, Sentence> builder = new TokenBuilder<>(Token.class, Sentence.class);
builder.buildTokens(jcas, "This is a test .\nThis is sentence two .");
for (Token t : select(jcas, Token.class)) {
POS pos = new POS(jcas, t.getBegin(), t.getEnd());
t.setPos(pos);
pos.addToIndexes();
}
RelationAdapter adapter = new RelationAdapter(layerSupportRegistry, featureSupportRegistry, null, depLayer, FEAT_REL_TARGET, FEAT_REL_SOURCE, () -> asList(dependencyLayerGovernor, dependencyLayerDependent), behaviors);
List<POS> posAnnotations = new ArrayList<>(select(jcas, POS.class));
POS source = posAnnotations.get(0);
POS target = posAnnotations.get(1);
RelationRenderer sut = new RelationRenderer(adapter, layerSupportRegistry, featureSupportRegistry, asList(new RelationOverlapBehavior()));
// Create two annotations stacked annotations
depLayer.setOverlapMode(ANY_OVERLAP);
AnnotationFS dep1 = adapter.add(document, username, source, target, jcas.getCas());
AnnotationFS dep2 = adapter.add(document, username, source, target, jcas.getCas());
{
depLayer.setOverlapMode(ANY_OVERLAP);
VDocument vdoc = new VDocument();
sut.render(jcas.getCas(), asList(), vdoc, 0, jcas.getDocumentText().length());
assertThat(vdoc.comments()).filteredOn(c -> !YIELD.equals(c.getCommentType())).isEmpty();
}
{
depLayer.setOverlapMode(STACKING_ONLY);
VDocument vdoc = new VDocument();
sut.render(jcas.getCas(), asList(), vdoc, 0, jcas.getDocumentText().length());
assertThat(vdoc.comments()).filteredOn(c -> !YIELD.equals(c.getCommentType())).isEmpty();
}
{
depLayer.setOverlapMode(OVERLAP_ONLY);
VDocument vdoc = new VDocument();
sut.render(jcas.getCas(), asList(), vdoc, 0, jcas.getDocumentText().length());
assertThat(vdoc.comments()).filteredOn(c -> !YIELD.equals(c.getCommentType())).usingFieldByFieldElementComparator().contains(new VComment(dep1, ERROR, "Stacking is not permitted."), new VComment(dep2, ERROR, "Stacking is not permitted."));
}
{
depLayer.setOverlapMode(NO_OVERLAP);
VDocument vdoc = new VDocument();
sut.render(jcas.getCas(), asList(), vdoc, 0, jcas.getDocumentText().length());
assertThat(vdoc.comments()).filteredOn(c -> !YIELD.equals(c.getCommentType())).usingFieldByFieldElementComparator().contains(new VComment(dep1, ERROR, "Stacking is not permitted."), new VComment(dep2, ERROR, "Stacking is not permitted."));
}
// Remove the stacked annotation and introduce one that is purely overlapping
adapter.delete(document, username, jcas.getCas(), new VID(dep2));
depLayer.setOverlapMode(ANY_OVERLAP);
AnnotationFS dep3 = adapter.add(document, username, source, posAnnotations.get(2), jcas.getCas());
{
depLayer.setOverlapMode(NO_OVERLAP);
VDocument vdoc = new VDocument();
sut.render(jcas.getCas(), asList(), vdoc, 0, jcas.getDocumentText().length());
assertThat(vdoc.comments()).filteredOn(c -> !YIELD.equals(c.getCommentType())).usingFieldByFieldElementComparator().contains(new VComment(dep1, ERROR, "Overlap is not permitted."), new VComment(dep3, ERROR, "Overlap is not permitted."));
}
}
use of de.tudarmstadt.ukp.clarin.webanno.api.annotation.adapter.RelationAdapter in project webanno by webanno.
the class RelationRendererTest method thatRelationCrossSentenceBehaviorOnRenderGeneratesErrors.
@Test
public void thatRelationCrossSentenceBehaviorOnRenderGeneratesErrors() throws Exception {
TokenBuilder<Token, Sentence> builder = new TokenBuilder<>(Token.class, Sentence.class);
builder.buildTokens(jcas, "This is a test .\nThis is sentence two .");
for (Token t : select(jcas, Token.class)) {
POS pos = new POS(jcas, t.getBegin(), t.getEnd());
t.setPos(pos);
pos.addToIndexes();
}
RelationAdapter adapter = new RelationAdapter(layerSupportRegistry, featureSupportRegistry, null, depLayer, FEAT_REL_TARGET, FEAT_REL_SOURCE, () -> asList(dependencyLayerGovernor, dependencyLayerDependent), behaviors);
List<POS> posAnnotations = new ArrayList<>(select(jcas, POS.class));
POS source = posAnnotations.get(0);
POS target = posAnnotations.get(posAnnotations.size() - 1);
depLayer.setCrossSentence(true);
AnnotationFS dep = adapter.add(document, username, source, target, jcas.getCas());
depLayer.setCrossSentence(false);
RelationRenderer sut = new RelationRenderer(adapter, layerSupportRegistry, featureSupportRegistry, asList(new RelationCrossSentenceBehavior()));
VDocument vdoc = new VDocument();
sut.render(jcas.getCas(), asList(), vdoc, 0, jcas.getDocumentText().length());
assertThat(vdoc.comments()).usingFieldByFieldElementComparator().contains(new VComment(dep, ERROR, "Crossing sentence boundaries is not permitted."));
}
use of de.tudarmstadt.ukp.clarin.webanno.api.annotation.adapter.RelationAdapter in project webanno by webanno.
the class RelationRenderer method getRelationLinks.
/**
* Get relation links to display in relation yield
*/
private Map<Integer, Set<Integer>> getRelationLinks(CAS aCas, int aWindowBegin, int aWindowEnd, Type type, Feature dependentFeature, Feature governorFeature, Feature arcSpanFeature) {
RelationAdapter typeAdapter = getTypeAdapter();
FeatureStructure dependentFs;
FeatureStructure governorFs;
Map<Integer, Set<Integer>> relations = new ConcurrentHashMap<>();
for (AnnotationFS fs : selectCovered(aCas, type, aWindowBegin, aWindowEnd)) {
if (typeAdapter.getAttachFeatureName() != null) {
dependentFs = fs.getFeatureValue(dependentFeature).getFeatureValue(arcSpanFeature);
governorFs = fs.getFeatureValue(governorFeature).getFeatureValue(arcSpanFeature);
} else {
dependentFs = fs.getFeatureValue(dependentFeature);
governorFs = fs.getFeatureValue(governorFeature);
}
if (dependentFs == null || governorFs == null) {
log.warn("Relation [" + typeAdapter.getLayer().getName() + "] with id [" + getAddr(fs) + "] has loose ends - cannot render.");
continue;
}
Set<Integer> links = relations.get(getAddr(governorFs));
if (links == null) {
links = new ConcurrentSkipListSet<>();
}
links.add(getAddr(dependentFs));
relations.put(getAddr(governorFs), links);
}
// Update other subsequent links
for (int i = 0; i < relations.keySet().size(); i++) {
for (Integer fs : relations.keySet()) {
updateLinks(relations, fs);
}
}
// to start displaying the text from the governor, include it
for (Integer fs : relations.keySet()) {
relations.get(fs).add(fs);
}
return relations;
}
use of de.tudarmstadt.ukp.clarin.webanno.api.annotation.adapter.RelationAdapter in project webanno by webanno.
the class RemoveDanglingRelationsRepair method repair.
@Override
public void repair(Project aProject, CAS aCas, List<LogMessage> aMessages) {
Set<FeatureStructure> nonIndexed = getNonIndexedFSes(aCas);
Set<FeatureStructure> toDelete = new LinkedHashSet<>();
for (AnnotationFS fs : aCas.getAnnotationIndex()) {
Type t = fs.getType();
Feature sourceFeat = t.getFeatureByBaseName(FEAT_REL_SOURCE);
Feature targetFeat = t.getFeatureByBaseName(FEAT_REL_TARGET);
// Is this a relation?
if (!(sourceFeat != null && targetFeat != null)) {
continue;
}
FeatureStructure source = fs.getFeatureValue(sourceFeat);
FeatureStructure target = fs.getFeatureValue(targetFeat);
// Are there null end-points or does it point to deleted spans?
if (source == null || target == null || nonIndexed.contains(source) || nonIndexed.contains(target)) {
toDelete.add(fs);
continue;
}
RelationAdapter relationAdapter = (RelationAdapter) annotationService.findAdapter(aProject, fs);
Feature relationSourceAttachFeature = null;
Feature relationTargetAttachFeature = null;
if (relationAdapter.getAttachFeatureName() != null) {
relationSourceAttachFeature = sourceFeat.getRange().getFeatureByBaseName(relationAdapter.getAttachFeatureName());
relationTargetAttachFeature = targetFeat.getRange().getFeatureByBaseName(relationAdapter.getAttachFeatureName());
}
// Here we get the annotations that the relation is pointing to in the UI
if (relationSourceAttachFeature != null) {
source = (AnnotationFS) source.getFeatureValue(relationSourceAttachFeature);
}
if (relationTargetAttachFeature != null) {
target = (AnnotationFS) target.getFeatureValue(relationTargetAttachFeature);
}
// annotations linked to in the UI?
if (source == null || target == null || nonIndexed.contains(source) || nonIndexed.contains(target)) {
toDelete.add(fs);
continue;
}
}
// Delete those relations that pointed to deleted spans
if (!toDelete.isEmpty()) {
toDelete.forEach(aCas::removeFsFromIndexes);
aMessages.add(new LogMessage(this, INFO, "Removed [%d] dangling relations.", toDelete.size()));
}
}
Aggregations