use of org.corpus_tools.annis.gui.widgets.grid.GridEvent in project ANNIS by korpling.
the class EventExtractor method splitRowsOnGaps.
/**
* Splits events of a row if they contain a gap. Gaps are found using the token index (provided as
* ANNIS specific {@link SFeature}. Inserted events have a special style to mark them as gaps.
*
* @param row
* @param graph
* @param token2index
*/
private static void splitRowsOnGaps(Row row, final SDocumentGraph graph, Map<SToken, Integer> token2index) {
ListIterator<GridEvent> itEvents = row.getEvents().listIterator();
while (itEvents.hasNext()) {
GridEvent event = itEvents.next();
int lastTokenIndex = -1;
// sort the coveredIDs
LinkedList<String> sortedCoveredToken = new LinkedList<>(event.getCoveredIDs());
Collections.sort(sortedCoveredToken, (o1, o2) -> {
SToken node1 = (SToken) graph.getNode(o1);
SToken node2 = (SToken) graph.getNode(o2);
if (node1 == node2) {
return 0;
}
if (node1 == null) {
return -1;
}
if (node2 == null) {
return +1;
}
long tokenIndex1 = token2index.get(node1);
long tokenIndex2 = token2index.get(node2);
return ((Long) (tokenIndex1)).compareTo(tokenIndex2);
});
// first calculate all gaps
List<GridEvent> gaps = new LinkedList<>();
for (String id : sortedCoveredToken) {
SToken node = (SToken) graph.getNode(id);
int tokenIndex = token2index.get(node);
// sanity check
if (tokenIndex >= event.getLeft() && tokenIndex <= event.getRight()) {
int diff = tokenIndex - lastTokenIndex;
if (lastTokenIndex >= 0 && diff > 1) {
// we detected a gap
GridEvent gap = new GridEvent(event.getId() + "_gap_" + gaps.size(), lastTokenIndex + 1, tokenIndex - 1, "");
gap.setGap(true);
gaps.add(gap);
}
lastTokenIndex = tokenIndex;
} else {
// reset gap search when discovered there were token we use for
// hightlighting but do not actually cover
lastTokenIndex = -1;
}
}
// end for each covered token id
ListIterator<GridEvent> itGaps = gaps.listIterator();
// remember the old right value
int oldRight = event.getRight();
int gapNr = 0;
while (itGaps.hasNext()) {
GridEvent gap = itGaps.next();
if (gapNr == 0) {
// shorten original event
event.setRight(gap.getLeft() - 1);
}
// insert the real gap
itEvents.add(gap);
int rightBorder = oldRight;
if (itGaps.hasNext()) {
// don't use the old event right border since the gap should only go until
// the next event
GridEvent nextGap = itGaps.next();
itGaps.previous();
rightBorder = nextGap.getLeft() - 1;
}
// insert a new event node that covers the rest of the event
GridEvent after = new GridEvent(event);
after.setId(event.getId() + "_after_" + gapNr);
after.setLeft(gap.getRight() + 1);
after.setRight(rightBorder);
itEvents.add(after);
gapNr++;
}
}
}
use of org.corpus_tools.annis.gui.widgets.grid.GridEvent in project ANNIS by korpling.
the class SingleGridComponent method createAnnotationGrid.
private boolean createAnnotationGrid() {
String resultID = input.getId();
grid = new AnnotationGrid(mediaController, pdfController, resultID);
grid.addStyleName(getMainStyle());
grid.addStyleName(Helper.CORPUS_FONT_FORCE);
grid.setEscapeHTML(Boolean.parseBoolean(input.getMappings().getOrDefault(MAPPING_ESCAPE_HTML, "true")));
LinkedList<Class<? extends SNode>> types = new LinkedList<>();
if (isShowingSpanAnnotations()) {
types.add(SSpan.class);
}
if (isShowingTokenAnnotations()) {
types.add(SToken.class);
}
grid.setAnnosWithNamespace(EventExtractor.computeDisplayedNamespace(input, types));
layout.addComponent(grid);
SDocumentGraph graph = input.getDocument().getDocumentGraph();
List<SNode> sortedSegmentationNodes = Helper.getSortedSegmentationNodes(this.segmentationName, graph);
Map<SToken, Integer> token2index = Helper.createToken2IndexMap(graph, this.enforcedText);
Preconditions.checkArgument(!token2index.isEmpty(), "Token list must be non-empty");
LinkedHashMap<String, ArrayList<Row>> rowsByAnnotation = computeAnnotationRows(token2index);
// Get Mappings
String gridTemplates = input.getMappings().getOrDefault(MAPPING_GRID_TEMPLATES, "");
// Parse Mappings
if (!gridTemplates.equals("")) {
String[] split = gridTemplates.split("\\|\\|");
for (String s : split) {
// example of s: entity="person"==>:), or infstat==><b>%%value%%</b>
String[] unit_split = s.split("==>");
Set<Map.Entry<String, ArrayList<Row>>> set = rowsByAnnotation.entrySet();
// Displaying elements of LinkedHashMap
Iterator<Map.Entry<String, ArrayList<Row>>> iterator = set.iterator();
while (iterator.hasNext()) {
// iterate over rows
Map.Entry<String, ArrayList<Row>> me = iterator.next();
String rowKey = me.getKey();
ArrayList<Row> rowValue = me.getValue();
for (Row rowValue1 : rowValue) {
ArrayList<GridEvent> rowEvents = rowValue1.getEvents();
if (unit_split[0].indexOf('=') < 0) {
// unit_split[0] is a single instruction, e.g., infstat
// check if the key of a row in rowsByAnnotation is unit_split[0]
// if it is, we need to change every value of this row, else we dont do anything
String rowName = rowKey.split("::")[1];
if (rowName.equals(unit_split[0])) {
// iterate over all values and replace the value with the unit_split[1]
for (GridEvent ev : rowEvents) {
String origValue = ev.getValue();
String newValue = unit_split[1].replaceAll("%%value%%", origValue);
ev.setValue(newValue);
}
}
} else {
// its a instruction like entity='person'
// first break this split into entity and person
// check if rowKey is entity, then when iterating over events, check if value is
// person
String rowName = rowKey.split("::")[1];
String targetRow = unit_split[0].split("=")[0];
String targetValue = unit_split[0].split("=")[1].replaceAll("\"", "");
if (rowName.equals(targetRow)) {
// iterate over all values and replace the value with the unit_split[1]
for (GridEvent ev : rowEvents) {
String origValue = ev.getValue();
if (origValue.equals(targetValue)) {
ev.setValue(unit_split[1]);
}
// String newValue = unit_split[1].replaceAll("%%value%%",origValue);
}
}
}
}
}
}
}
// add tokens as row
Row tokenRow = computeTokenRow(sortedSegmentationNodes, graph, rowsByAnnotation, token2index);
String tokenRowCaption = "tok";
if (isHidingToken()) {
// We have to add the invisible token row avoid issues with the layout
// (see https://github.com/korpling/ANNIS/issues/524)
// but we don't want the invisible token layer to override an actual "tok"
// annotation layer (see https://github.com/korpling/ANNIS/issues/596)
tokenRow.setStyle("invisible_token");
tokenRowCaption = "";
grid.setTokRowKey("");
}
if (isTokenFirst()) {
// copy original list but add token row at the beginning
LinkedHashMap<String, ArrayList<Row>> newList = new LinkedHashMap<>();
newList.put(tokenRowCaption, Lists.newArrayList(tokenRow));
newList.putAll(rowsByAnnotation);
rowsByAnnotation = newList;
} else {
// just add the token row to the end of the list
rowsByAnnotation.put(tokenRowCaption, Lists.newArrayList(tokenRow));
}
EventExtractor.removeEmptySpace(rowsByAnnotation, tokenRow);
// check if the token row only contains empty values
boolean tokenRowIsEmpty = true;
for (GridEvent tokenEvent : tokenRow.getEvents()) {
if (tokenEvent.getValue() != null && !tokenEvent.getValue().trim().isEmpty()) {
tokenRowIsEmpty = false;
break;
}
}
if (!isHidingToken() && canShowEmptyTokenWarning()) {
lblEmptyToken.setVisible(tokenRowIsEmpty);
}
grid.setRowsByAnnotation(rowsByAnnotation);
return !tokenRowIsEmpty;
}
use of org.corpus_tools.annis.gui.widgets.grid.GridEvent in project ANNIS by korpling.
the class SingleGridComponent method computeTokenRow.
private Row computeTokenRow(List<SNode> tokens, SDocumentGraph graph, LinkedHashMap<String, ArrayList<Row>> rowsByAnnotation, Map<SToken, Integer> token2index) {
/*
* we will only add tokens of one texts which is mentioned by any included annotation.
*/
Set<String> validTextIDs = new HashSet<>();
if (enforcedText == null) {
Iterator<ArrayList<Row>> itAllRows = rowsByAnnotation.values().iterator();
while (itAllRows.hasNext()) {
ArrayList<Row> rowsForAnnotation = itAllRows.next();
for (Row r : rowsForAnnotation) {
validTextIDs.addAll(r.getTextIDs());
}
}
/**
* we want to show all token if no valid text was found and we have only one text and the
* first one if there is more than one text.
*/
List<STextualDS> allTexts = graph.getTextualDSs();
if (validTextIDs.isEmpty() && allTexts != null && (allTexts.size() == 1 || allTexts.size() == 2)) {
validTextIDs.add(allTexts.get(0).getId());
}
} else {
validTextIDs.add(enforcedText.getId());
}
Row tokenRow = new Row();
for (SNode t : tokens) {
// get the Salt ID of the STextualDS of this token
STextualDS tokenText = Helper.getTextualDSForNode(t, graph);
// only add token if text ID matches the valid one
if (tokenText != null && validTextIDs.contains(tokenText.getId()) && hasSegmentation(t, this.segmentationName)) {
Range<Integer> coveredRange = Helper.getLeftRightSpan(t, graph, token2index);
String text = extractTextForToken(t, segmentationName);
GridEvent event = new GridEvent(t.getId(), coveredRange.lowerEndpoint(), coveredRange.upperEndpoint(), text);
event.setTextID(tokenText.getId());
// check if the token is a matched node
Long match = isCoveredTokenMarked() ? markCoveredTokens(input.getMarkedAndCovered(), t) : tokenMatch(t);
event.setMatch(match);
tokenRow.addEvent(event);
}
}
return tokenRow;
}
use of org.corpus_tools.annis.gui.widgets.grid.GridEvent in project ANNIS by korpling.
the class EventExtractor method splitRowsOnIslands.
/**
* Splits events of a row if they overlap an island. Islands are areas between the token which are
* included in the result.
*
* @param row
* @param graph
* @param text
* @param token2index
*/
private static void splitRowsOnIslands(Row row, final SDocumentGraph graph, STextualDS text, Map<SToken, Integer> token2index) {
BitSet tokenCoverage = new BitSet();
// get the sorted token
List<SToken> sortedTokenList = graph.getSortedTokenByText();
// add all token belonging to the right text to the bit set
ListIterator<SToken> itToken = sortedTokenList.listIterator();
while (itToken.hasNext()) {
SToken t = itToken.next();
if (text == null || text == Helper.getTextualDSForNode(t, graph)) {
int tokenIndex = token2index.get(t);
tokenCoverage.set(tokenIndex);
}
}
ListIterator<GridEvent> itEvents = row.getEvents().listIterator();
while (itEvents.hasNext()) {
GridEvent event = itEvents.next();
BitSet eventBitSet = new BitSet();
eventBitSet.set(event.getLeft(), event.getRight() + 1);
// restrict event bitset on the locations where token are present
eventBitSet.and(tokenCoverage);
// and we need to split it
if (eventBitSet.nextClearBit(event.getLeft()) <= event.getRight()) {
// remove the original event
row.removeEvent(itEvents);
// The event bitset now marks all the locations which the event should
// cover.
// Make a list of new events for each connected range in the bitset
int subElement = 0;
int offset = eventBitSet.nextSetBit(0);
while (offset >= 0) {
int end = eventBitSet.nextClearBit(offset) - 1;
if (offset < end) {
GridEvent newEvent = new GridEvent(event);
newEvent.setId(event.getId() + "_islandsplit_" + subElement++);
newEvent.setLeft(offset);
newEvent.setRight(end);
row.addEvent(itEvents, newEvent);
}
offset = eventBitSet.nextSetBit(end + 1);
}
}
// end if we need to split
}
}
use of org.corpus_tools.annis.gui.widgets.grid.GridEvent in project ANNIS by korpling.
the class EventExtractor method removeEmptySpace.
public static void removeEmptySpace(LinkedHashMap<String, ArrayList<Row>> rowsByAnnotation, Row tokenRow) {
List<Range<Integer>> gaps = new LinkedList<>();
BitSet totalOccupancyGrid = new BitSet();
for (Map.Entry<String, ArrayList<Row>> layer : rowsByAnnotation.entrySet()) {
for (Row r : layer.getValue()) {
totalOccupancyGrid.or(r.getOccupancyGridCopy());
}
}
// bug report.
if (tokenRow != null) {
totalOccupancyGrid.or(tokenRow.getOccupancyGridCopy());
}
// The Range class can give us the next bit that is not set. Use this
// to detect gaps. A gap starts from the next non-set bit and goes to
// the next set bit.
Range<Integer> gap = Range.closed(-1, totalOccupancyGrid.nextSetBit(0));
while (true) {
int gapStart = totalOccupancyGrid.nextClearBit(gap.upperEndpoint() + 1);
int gapEnd = totalOccupancyGrid.nextSetBit(gapStart);
if (gapEnd <= 0) {
break;
}
gap = Range.closed(gapStart, gapEnd - 1);
gaps.add(gap);
}
int gapID = 0;
int totalOffset = 0;
for (Range<Integer> gRaw : gaps) {
// adjust the space range itself
Range<Integer> g = Range.closed(gRaw.lowerEndpoint() - totalOffset, gRaw.upperEndpoint() - totalOffset);
int offset = g.upperEndpoint() - g.lowerEndpoint();
totalOffset += offset;
for (Entry<String, ArrayList<Row>> rowEntry : rowsByAnnotation.entrySet()) {
ArrayList<Row> rows = rowEntry.getValue();
for (Row r : rows) {
List<GridEvent> eventsCopy = new LinkedList<>(r.getEvents());
for (GridEvent e : eventsCopy) {
if (e.getLeft() >= g.upperEndpoint()) {
r.removeEvent(e);
e.setLeft(e.getLeft() - offset);
e.setRight(e.getRight() - offset);
r.addEvent(e);
}
}
// add a special space event
String spaceCaption = "";
if ("tok".equalsIgnoreCase(rowEntry.getKey())) {
spaceCaption = "(...)";
}
GridEvent spaceEvent = new GridEvent("gap-" + gapID, g.lowerEndpoint(), g.lowerEndpoint(), spaceCaption);
spaceEvent.setSpace(true);
r.addEvent(spaceEvent);
gapID++;
}
}
}
}
Aggregations