Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#4139 - Reusable popover component for annotation editors #4152

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Comparator.comparingInt;
import static org.apache.commons.lang3.StringUtils.abbreviate;
import static org.apache.uima.fit.util.CasUtil.selectCovered;

import java.util.ArrayList;
Expand All @@ -39,6 +40,7 @@
import org.apache.uima.cas.Type;
import org.apache.uima.cas.TypeSystem;
import org.apache.uima.cas.text.AnnotationFS;
import org.apache.uima.jcas.tcas.Annotation;
import org.apache.wicket.Page;
import org.apache.wicket.core.request.handler.IPageRequestHandler;
import org.apache.wicket.request.cycle.PageRequestHandlerTracker;
Expand Down Expand Up @@ -157,27 +159,24 @@ public void render(final CAS aCas, List<AnnotationFeature> aFeatures, VDocument
}
}

private Optional<String> renderYield(AnnotationFS fs, Map<Integer, Set<Integer>> relationLinks,
List<Integer> yieldDeps)
private Optional<String> renderYield(AnnotationFS fs)
{
FeatureStructure dependentFs = getDependentFs(fs);

if (relationLinks.keySet().contains(ICasUtil.getAddr(dependentFs))
&& !yieldDeps.contains(ICasUtil.getAddr(dependentFs))) {
yieldDeps.add(ICasUtil.getAddr(dependentFs));
var relationLinks = getRelationLinks(fs.getCAS());

// sort the annotations (begin, end)
List<Integer> sortedDepFs = new ArrayList<>(
relationLinks.get(ICasUtil.getAddr(dependentFs)));
sortedDepFs.sort(comparingInt(
arg0 -> ICasUtil.selectAnnotationByAddr(fs.getCAS(), arg0).getBegin()));
if (!relationLinks.keySet().contains(ICasUtil.getAddr(dependentFs))) {
return Optional.empty();
}

String cm = getYieldMessage(fs.getCAS(), sortedDepFs);
// sort the annotations (begin, end)
var sortedDepFs = new ArrayList<>(relationLinks.get(ICasUtil.getAddr(dependentFs)));
sortedDepFs.sort(comparingInt(
arg0 -> ICasUtil.selectAnnotationByAddr(fs.getCAS(), arg0).getBegin()));

return Optional.of(cm);
}
var cm = getYieldMessage(fs.getCAS(), sortedDepFs);

return Optional.empty();
return Optional.of(cm);
}

@Override
Expand All @@ -188,9 +187,9 @@ public List<VObject> render(VDocument aVDocument, AnnotationFS aFS,
return Collections.emptyList();
}

RelationAdapter typeAdapter = getTypeAdapter();
FeatureStructure dependentFs = getDependentFs(aFS);
FeatureStructure governorFs = getGovernorFs(aFS);
var typeAdapter = getTypeAdapter();
var dependentFs = getDependentFs(aFS);
var governorFs = getGovernorFs(aFS);

if (dependentFs == null || governorFs == null) {
StringBuilder message = new StringBuilder();
Expand Down Expand Up @@ -259,23 +258,29 @@ public List<VLazyDetailGroup> lookupLazyDetails(CAS aCas, VID aVid, int aWindowB
return Collections.emptyList();
}

// FIXME Should also handle relations that are only partially visible using
// selectAnnotationsInWindow()
var relationLinks = getRelationLinks(aCas, aWindowBeginOffset, aWindowEndOffset);

// if this is a governor for more than one dependent, avoid duplicate yield
var yieldDeps = new ArrayList<Integer>();

var fs = ICasUtil.selectByAddr(aCas, AnnotationFS.class, aVid.getId());

var yield = renderYield(fs, relationLinks, yieldDeps);
var group = new VLazyDetailGroup();

var details = super.lookupLazyDetails(aCas, aVid, aWindowBeginOffset, aWindowEndOffset);
var dependentFs = getDependentFs(fs);
if (dependentFs instanceof AnnotationFS) {
group.addDetail(new VLazyDetail("Target",
abbreviate(((AnnotationFS) dependentFs).getCoveredText(), 300)));
}

if (yield.isPresent()) {
details.add(new VLazyDetailGroup(new VLazyDetail("Yield", yield.get())));
var governorFs = getGovernorFs(fs);
if (governorFs instanceof AnnotationFS) {
group.addDetail(new VLazyDetail("Origin",
abbreviate(((AnnotationFS) governorFs).getCoveredText(), 300)));
}

renderYield(fs).ifPresent(
yield -> group.addDetail(new VLazyDetail("Yield", abbreviate(yield, "...", 300))));

var details = super.lookupLazyDetails(aCas, aVid, aWindowBeginOffset, aWindowEndOffset);
if (!group.getDetails().isEmpty()) {
details.add(0, group);
}
return details;
}

Expand Down Expand Up @@ -314,46 +319,48 @@ else if (end + 1 != ICasUtil.selectAnnotationByAddr(aCas, depFs).getBegin()) {
/**
* Get relation links to display in relation yield
*/
private Map<Integer, Set<Integer>> getRelationLinks(CAS aCas, int aWindowBegin, int aWindowEnd)
private Map<Integer, Set<Integer>> getRelationLinks(CAS aCas)
{
RelationAdapter typeAdapter = getTypeAdapter();
Map<Integer, Set<Integer>> relations = new ConcurrentHashMap<>();
var typeAdapter = getTypeAdapter();
var relations = new ConcurrentHashMap<Integer, Set<Integer>>();

for (AnnotationFS fs : selectCovered(aCas, type, aWindowBegin, aWindowEnd)) {
FeatureStructure dependentFs = getGovernorFs(fs);
FeatureStructure governorFs = getDependentFs(fs);
for (var fs : aCas.<Annotation> select(type)) {
var govFs = getGovernorFs(fs);
var depFs = getDependentFs(fs);

if (dependentFs == null || governorFs == null) {
if (govFs == null || depFs == null) {
log.warn("Relation [" + typeAdapter.getLayer().getName() + "] with id ["
+ ICasUtil.getAddr(fs) + "] has loose ends - cannot render.");
+ VID.of(fs) + "] has loose ends - cannot render.");
continue;
}

Set<Integer> links = relations.get(ICasUtil.getAddr(governorFs));
var links = relations.get(ICasUtil.getAddr(depFs));
if (links == null) {
links = new ConcurrentSkipListSet<>();
}

links.add(ICasUtil.getAddr(dependentFs));
relations.put(ICasUtil.getAddr(governorFs), links);
links.add(ICasUtil.getAddr(govFs));
relations.put(ICasUtil.getAddr(depFs), links);
}

// Update other subsequent links
for (int i = 0; i < relations.keySet().size(); i++) {
for (Integer fs : relations.keySet()) {
for (var fs : relations.keySet()) {
updateLinks(relations, fs);
}
}

// to start displaying the text from the governor, include it
for (Integer fs : relations.keySet()) {
for (var fs : relations.keySet()) {
relations.get(fs).add(fs);
}

return relations;
}

private void updateLinks(Map<Integer, Set<Integer>> aRelLinks, Integer aGov)
{
for (Integer dep : aRelLinks.get(aGov)) {
for (var dep : aRelLinks.get(aGov)) {
if (aRelLinks.containsKey(dep)
&& !aRelLinks.get(aGov).containsAll(aRelLinks.get(dep))) {
aRelLinks.get(aGov).addAll(aRelLinks.get(dep));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import static de.tudarmstadt.ukp.clarin.webanno.support.WebAnnoConst.RELATION_TYPE;
import static de.tudarmstadt.ukp.clarin.webanno.support.WebAnnoConst.SPAN_TYPE;
import static de.tudarmstadt.ukp.inception.rendering.vmodel.VCommentType.ERROR;
import static de.tudarmstadt.ukp.inception.rendering.vmodel.VCommentType.YIELD;
import static java.util.Arrays.asList;
import static org.apache.uima.fit.util.JCasUtil.select;
import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -201,17 +200,15 @@ public void thatRelationOverlapBehaviorOnRenderGeneratesErrors() throws Exceptio
VDocument vdoc = new VDocument();
sut.render(jcas.getCas(), asList(), vdoc, 0, jcas.getDocumentText().length());

assertThat(vdoc.comments()).filteredOn(c -> !YIELD.equals(c.getCommentType()))
.isEmpty();
assertThat(vdoc.comments()).filteredOn(c -> ERROR.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();
assertThat(vdoc.comments()).filteredOn(c -> ERROR.equals(c.getCommentType())).isEmpty();

}

Expand All @@ -221,7 +218,7 @@ public void thatRelationOverlapBehaviorOnRenderGeneratesErrors() throws Exceptio
sut.render(jcas.getCas(), asList(), vdoc, 0, jcas.getDocumentText().length());

assertThat(vdoc.comments()) //
.filteredOn(c -> !YIELD.equals(c.getCommentType()))
.filteredOn(c -> ERROR.equals(c.getCommentType()))
.usingRecursiveFieldByFieldElementComparator().contains( //
new VComment(dep1, ERROR, "Stacking is not permitted."),
new VComment(dep2, ERROR, "Stacking is not permitted."));
Expand All @@ -233,7 +230,7 @@ public void thatRelationOverlapBehaviorOnRenderGeneratesErrors() throws Exceptio
sut.render(jcas.getCas(), asList(), vdoc, 0, jcas.getDocumentText().length());

assertThat(vdoc.comments()) //
.filteredOn(c -> !YIELD.equals(c.getCommentType()))
.filteredOn(c -> ERROR.equals(c.getCommentType()))
.usingRecursiveFieldByFieldElementComparator().contains( //
new VComment(dep1, ERROR, "Stacking is not permitted."),
new VComment(dep2, ERROR, "Stacking is not permitted."));
Expand All @@ -251,7 +248,7 @@ public void thatRelationOverlapBehaviorOnRenderGeneratesErrors() throws Exceptio
sut.render(jcas.getCas(), asList(), vdoc, 0, jcas.getDocumentText().length());

assertThat(vdoc.comments()) //
.filteredOn(c -> !YIELD.equals(c.getCommentType()))
.filteredOn(c -> ERROR.equals(c.getCommentType()))
.usingRecursiveFieldByFieldElementComparator().contains( //
new VComment(dep1, ERROR, "Overlap is not permitted."),
new VComment(dep3, ERROR, "Overlap is not permitted."));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@

public enum VCommentType
{
INFO, ERROR, YIELD
INFO, ERROR
}
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,6 @@ private void renderComments(GetDocumentResponse aResponse, VDocument aVDoc,
case INFO:
type = AnnotationComment.ANNOTATOR_NOTES;
break;
case YIELD:
type = "Yield";
break;
default:
type = AnnotationComment.ANNOTATOR_NOTES;
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@
import de.tudarmstadt.ukp.inception.rendering.vmodel.VID;
import de.tudarmstadt.ukp.inception.support.json.BeanAsArraySerializer;

/**
* Use this "comments" to highlight "yield" of relation nodes
*/
@JsonSerialize(using = BeanAsArraySerializer.class)
@JsonPropertyOrder(value = { "vid", "commentType", "comment" })
public class AnnotationComment
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ export type EntityAttributesDto = {
cl: ClippedState;
}

/**
* Use this "comments" to highlight "yield" of relation nodes
*/
export type AnnotationCommentDto = [
id: VID,
commentType: CommentType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,9 +195,6 @@ private void renderComments(VDocument aVDoc, HashMap<VID, CompactAnnotation> vid
case INFO:
code = CompactComment.INFO;
break;
case YIELD:
code = CompactComment.INFO;
break;
default:
throw new IllegalStateException(
"Unsupported comment type [" + comment.getCommentType() + "]");
Expand Down
Loading