Skip to content

Commit

Permalink
Merge pull request #4810 from inception-project/feature/4809-Connecti…
Browse files Browse the repository at this point in the history
…ng-annotation-and-entity-with-arc-over-different-pages

#4809 - Connecting annotation and entity with arc over different pages
  • Loading branch information
reckart authored May 28, 2024
2 parents 7c743ed + 1abab77 commit 7136d0b
Show file tree
Hide file tree
Showing 23 changed files with 551 additions and 245 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import java.util.Collections;
import java.util.List;

import org.apache.uima.cas.CAS;
import org.apache.uima.cas.TypeSystem;
import org.apache.uima.cas.text.AnnotationFS;

Expand All @@ -47,12 +46,6 @@ protected boolean typeSystemInit(TypeSystem aTypeSystem)
return false;
}

@Override
public List<AnnotationFS> selectAnnotationsInWindow(CAS aCas, int aWindowBegin, int aWindowEnd)
{
return Collections.emptyList();
}

@Override
public void render(RenderRequest aRequest, List<AnnotationFeature> aFeatures,
VDocument aResponse, int windowBeginOffset, int windowEndOffset)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,11 @@
package de.tudarmstadt.ukp.clarin.webanno.api.annotation.rendering;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.apache.uima.cas.CAS;
import org.apache.uima.cas.TypeSystem;
import org.apache.uima.cas.text.AnnotationFS;

import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationFeature;
import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationLayer;
Expand Down Expand Up @@ -142,7 +140,4 @@ public <T> Optional<T> getTraits(AnnotationLayer aLayer, Class<T> aInterface)

return Optional.empty();
}

public abstract List<AnnotationFS> selectAnnotationsInWindow(CAS aCas, int aWindowBegin,
int aWindowEnd);
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ protected boolean typeSystemInit(TypeSystem aTypeSystem)
return true;
}

@Override
public List<AnnotationFS> selectAnnotationsInWindow(CAS aCas, int aWindowBegin, int aWindowEnd)
{
var typeAdapter = getTypeAdapter();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@
*/
package de.tudarmstadt.ukp.inception.annotation.layer.relation;

import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.groupingBy;
import static org.apache.commons.lang3.StringUtils.abbreviate;
import static org.apache.uima.fit.util.CasUtil.selectCovered;
import static org.apache.uima.cas.text.AnnotationPredicates.overlapping;

import java.lang.invoke.MethodHandles;
import java.util.ArrayDeque;
Expand Down Expand Up @@ -56,6 +55,8 @@
import de.tudarmstadt.ukp.inception.rendering.vmodel.VLazyDetail;
import de.tudarmstadt.ukp.inception.rendering.vmodel.VLazyDetailGroup;
import de.tudarmstadt.ukp.inception.rendering.vmodel.VObject;
import de.tudarmstadt.ukp.inception.rendering.vmodel.VRange;
import de.tudarmstadt.ukp.inception.rendering.vmodel.VSpan;
import de.tudarmstadt.ukp.inception.schema.api.feature.FeatureSupportRegistry;
import de.tudarmstadt.ukp.inception.schema.api.layer.LayerSupportRegistry;
import de.tudarmstadt.ukp.inception.support.uima.ICasUtil;
Expand All @@ -69,6 +70,18 @@ public class RelationRenderer
{
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());

private static final VID VID_BEFORE = VID.builder() //
.withExtensionId("rel") //
.withAnnotationId(0) //
.withExtensionPayload("before") //
.build();

private static final VID VID_AFTER = VID.builder() //
.withExtensionId("rel") //
.withAnnotationId(1) //
.withExtensionPayload("after") //
.build();

private final List<RelationLayerBehavior> behaviors;
private final RelationLayerTraits traits;

Expand Down Expand Up @@ -118,10 +131,31 @@ protected boolean typeSystemInit(TypeSystem aTypeSystem)
return true;
}

@Override
public List<AnnotationFS> selectAnnotationsInWindow(CAS aCas, int aWindowBegin, int aWindowEnd)
private List<Annotation> selectAnnotationsInWindow(RenderRequest aRequest, int aWindowBegin,
int aWindowEnd)
{
return selectCovered(aCas, type, aWindowBegin, aWindowEnd);
var cas = aRequest.getCas();

if (!aRequest.isOutOfRangeRelations()) {
return cas.<Annotation> select(type).coveredBy(aWindowBegin, aWindowEnd).toList();
}

var result = new ArrayList<Annotation>();
for (var rel : cas.<Annotation> select(type)) {
var sourceFs = getSourceFs(rel);
var targetFs = getTargetFs(rel);

if (sourceFs instanceof Annotation source && targetFs instanceof Annotation target) {
var relBegin = Math.min(source.getBegin(), target.getBegin());
var relEnd = Math.max(source.getEnd(), target.getEnd());

if (overlapping(relBegin, relEnd, aWindowBegin, aWindowEnd)) {
result.add(rel);
}
}
}

return result;
}

@Override
Expand All @@ -137,16 +171,17 @@ public void render(RenderRequest aRequest, List<AnnotationFeature> aFeatures,
// Index mapping annotations to the corresponding rendered arcs
var annoToArcIdx = new HashMap<AnnotationFS, VArc>();

var annotations = selectAnnotationsInWindow(aRequest.getCas(), aWindowBegin, aWindowEnd);
var annotations = selectAnnotationsInWindow(aRequest, aWindowBegin, aWindowEnd);

for (var fs : annotations) {
for (var arc : render(aRequest, aFeatures, aResponse, aWindowBegin, aWindowEnd, fs)) {
if (!(arc instanceof VArc)) {
for (var obj : render(aRequest, aFeatures, aResponse, aWindowBegin, aWindowEnd, fs)) {
if (!(obj instanceof VArc)) {
aResponse.add(obj);
continue;
}

aResponse.add(arc);
annoToArcIdx.put(fs, (VArc) arc);
aResponse.add(obj);
annoToArcIdx.put(fs, (VArc) obj);

renderRequiredFeatureErrors(aRequest, aFeatures, fs, aResponse);
}
Expand Down Expand Up @@ -216,18 +251,21 @@ public List<VObject> render(RenderRequest aRequest, List<AnnotationFeature> aFea

switch (traits.getRenderMode()) {
case ALWAYS:
return renderRelationAsArcs(aFS, typeAdapter, sourceFs, targetFs, labelFeatures);
return renderRelationAsArcs(aRequest, aVDocument, aFS, typeAdapter, sourceFs, targetFs,
labelFeatures, aWindowBegin, aWindowEnd);
case WHEN_SELECTED:
if (aRequest.getState() == null || isSelected(aRequest, aFS, sourceFs, targetFs)) {
return renderRelationAsArcs(aFS, typeAdapter, sourceFs, targetFs, labelFeatures);
return renderRelationAsArcs(aRequest, aVDocument, aFS, typeAdapter, sourceFs,
targetFs, labelFeatures, aWindowBegin, aWindowEnd);
}
return renderRelationOnLabel(aVDocument, typeAdapter, sourceFs, targetFs,
labelFeatures);
case NEVER:
return renderRelationOnLabel(aVDocument, typeAdapter, sourceFs, targetFs,
labelFeatures);
default:
return renderRelationAsArcs(aFS, typeAdapter, sourceFs, targetFs, labelFeatures);
return renderRelationAsArcs(aRequest, aVDocument, aFS, typeAdapter, sourceFs, targetFs,
labelFeatures, aWindowBegin, aWindowEnd);
}
}

Expand Down Expand Up @@ -278,17 +316,80 @@ private List<VObject> renderRelationOnLabel(VDocument aVDocument, RelationAdapte
return emptyList();
}

private List<VObject> renderRelationAsArcs(AnnotationFS aFS, RelationAdapter typeAdapter,
FeatureStructure sourceFs, FeatureStructure targetFs, Map<String, String> labelFeatures)
private List<VObject> renderRelationAsArcs(RenderRequest aRequest, VDocument aVDocument,
AnnotationFS aFS, RelationAdapter typeAdapter, FeatureStructure sourceFs,
FeatureStructure targetFs, Map<String, String> labelFeatures, int aWindowBegin,
int aWindowEnd)
{
var arc = VArc.builder().forAnnotation(aFS) //
var objects = new ArrayList<VObject>();

var source = createRelationEndpoint(aRequest, aVDocument, (AnnotationFS) sourceFs,
aWindowBegin, aWindowEnd, typeAdapter);

var target = createRelationEndpoint(aRequest, aVDocument, (AnnotationFS) targetFs,
aWindowBegin, aWindowEnd, typeAdapter);

objects.add(VArc.builder().forAnnotation(aFS) //
.withLayer(typeAdapter.getLayer()) //
.withSource(sourceFs) //
.withTarget(targetFs) //
.withSource(source) //
.withTarget(target) //
.withFeatures(labelFeatures) //
.build();
.build());

return objects;
}

private VID createRelationEndpoint(RenderRequest aRequest, VDocument aVDocument,
AnnotationFS aEndpoint, int aWindowBegin, int aWindowEnd, RelationAdapter aTypeAdapter)
{
if (aEndpoint.getEnd() < aWindowBegin) {
if (aRequest.isClipRelations()) {
if (aVDocument.getSpan(VID_BEFORE) == null) {
var beforeAnchor = VSpan.builder() //
.withVid(VID_BEFORE) //
.withLayer(aTypeAdapter.getLayer()) //
.withRange(new VRange(0, 0)) //
.build();
aVDocument.add(beforeAnchor);
}
return VID_BEFORE;
}

var placeholder = VSpan.builder() //
.forAnnotation(aEndpoint) //
.withLayer(aTypeAdapter.getLayer()) //
.withRange(new VRange(aEndpoint.getBegin() - aWindowBegin,
aEndpoint.getEnd() - aWindowBegin)) //
.build();
aVDocument.add(placeholder);
return placeholder.getVid();
}

if (aEndpoint.getBegin() >= aWindowEnd) {
if (aRequest.isClipRelations()) {
if (aVDocument.getSpan(VID_AFTER) == null) {
var afterAnchor = VSpan.builder() //
.withVid(VID_AFTER) //
.withLayer(aTypeAdapter.getLayer()) //
.withRange(new VRange(aWindowEnd - aWindowBegin,
aWindowEnd - aWindowBegin)) //
.build();
aVDocument.add(afterAnchor);
}
return VID_AFTER;
}

var placeholder = VSpan.builder() //
.forAnnotation(aEndpoint) //
.withLayer(aTypeAdapter.getLayer()) //
.withRange(new VRange(aEndpoint.getBegin() - aWindowBegin,
aEndpoint.getEnd() - aWindowBegin)) //
.build();
aVDocument.add(placeholder);
return placeholder.getVid();
}

return asList(arc);
return VID.of(aEndpoint);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public SpanRenderer(SpanAdapter aTypeAdapter, LayerSupportRegistry aLayerSupport
behaviors = emptyList();
}
else {
List<SpanLayerBehavior> temp = new ArrayList<>(aBehaviors);
var temp = new ArrayList<SpanLayerBehavior>(aBehaviors);
AnnotationAwareOrderComparator.sort(temp);
behaviors = temp;
}
Expand All @@ -78,7 +78,7 @@ public SpanRenderer(SpanAdapter aTypeAdapter, LayerSupportRegistry aLayerSupport
@Override
protected boolean typeSystemInit(TypeSystem aTypeSystem)
{
SpanAdapter typeAdapter = getTypeAdapter();
var typeAdapter = getTypeAdapter();

type = aTypeSystem.getType(typeAdapter.getAnnotationTypeName());
if (type == null) {
Expand All @@ -90,10 +90,10 @@ protected boolean typeSystemInit(TypeSystem aTypeSystem)
return true;
}

@Override
public List<AnnotationFS> selectAnnotationsInWindow(CAS aCas, int aWindowBegin, int aWindowEnd)
{
return aCas.select(type).coveredBy(0, aWindowEnd).includeAnnotationsWithEndBeyondBounds()
return aCas.select(type).coveredBy(0, aWindowEnd) //
.includeAnnotationsWithEndBeyondBounds() //
.map(fs -> (AnnotationFS) fs)
.filter(ann -> AnnotationPredicates.overlapping(ann, aWindowBegin, aWindowEnd))
.toList();
Expand Down Expand Up @@ -188,9 +188,9 @@ private void renderSlots(AnnotationFS aFS, List<VObject> aSpansAndSlots)
continue nextFeature;
}

if (ARRAY.equals(feat.getMultiValueMode()) && WITH_ROLE.equals(feat.getLinkMode())) {
if (feat.getMultiValueMode() == ARRAY && feat.getLinkMode() == WITH_ROLE) {
List<LinkWithRoleModel> links = typeAdapter.getFeatureValue(feat, aFS);
for (int li = 0; li < links.size(); li++) {
for (var li = 0; li < links.size(); li++) {
var link = links.get(li);
var targetFS = selectFsByAddr(aFS.getCAS(), link.targetAddr);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@
import de.tudarmstadt.ukp.inception.rendering.pipeline.RenderingPipeline;
import de.tudarmstadt.ukp.inception.rendering.request.RenderRequest;
import de.tudarmstadt.ukp.inception.rendering.request.RenderRequestedEvent;
import de.tudarmstadt.ukp.inception.rendering.vmodel.VDocument;
import de.tudarmstadt.ukp.inception.rendering.vmodel.serialization.VDocumentSerializer;
import de.tudarmstadt.ukp.inception.schema.api.adapter.AnnotationException;
import de.tudarmstadt.ukp.inception.schema.api.config.AnnotationSchemaProperties;
Expand Down Expand Up @@ -189,19 +188,20 @@ public void requestRender(AjaxRequestTarget aTarget)
protected <T> T render(CAS aCas, int aWindowBeginOffset, int aWindowEndOffset,
VDocumentSerializer<T> aTerminalStep)
{
RenderRequest request = RenderRequest.builder() //
var request = RenderRequest.builder() //
.withState(getModelObject()) //
.withWindow(aWindowBeginOffset, aWindowEndOffset) //
.withCas(aCas) //
.withSessionOwner(userService.getCurrentUser()) //
.withVisibleLayers(getLayersToRender(getModelObject())) //
.withOutOfRangeRelations(true) //
.build();

VDocument vdoc = renderingPipeline.render(request);
var vdoc = renderingPipeline.render(request);
return aTerminalStep.render(vdoc, request);
}

private List<AnnotationLayer> getLayersToRender(AnnotatorState state)
protected List<AnnotationLayer> getLayersToRender(AnnotatorState state)
{
List<AnnotationLayer> layersToRender = new ArrayList<>();
for (AnnotationLayer layer : state.getAnnotationLayers()) {
Expand Down
Loading

0 comments on commit 7136d0b

Please sign in to comment.