@Override public int compare(AnnotationFS arg0, AnnotationFS arg1) { int beginDiff = arg0.getBegin() - arg1.getBegin(); if (beginDiff == 0) { return arg1.getEnd() - arg0.getEnd(); } else { return beginDiff; } }
/** Performs name finding on the given cas object. */ public final void process(CAS cas) { if (isRemoveExistingAnnotations) { final AnnotationComboIterator sentenceNameCombo = new AnnotationComboIterator(cas, mSentenceType, mNameType); List<AnnotationFS> removeAnnotations = new LinkedList<AnnotationFS>(); for (AnnotationIteratorPair annotationIteratorPair : sentenceNameCombo) { for (AnnotationFS nameAnnotation : annotationIteratorPair.getSubIterator()) { removeAnnotations.add(nameAnnotation); } } for (AnnotationFS annotation : removeAnnotations) { cas.removeFsFromIndexes(annotation); } } final AnnotationComboIterator sentenceTokenCombo = new AnnotationComboIterator(cas, mSentenceType, mTokenType); for (AnnotationIteratorPair annotationIteratorPair : sentenceTokenCombo) { final List<AnnotationFS> sentenceTokenAnnotationList = new LinkedList<AnnotationFS>(); final List<String> sentenceTokenList = new LinkedList<String>(); for (AnnotationFS tokenAnnotation : annotationIteratorPair.getSubIterator()) { sentenceTokenAnnotationList.add(tokenAnnotation); sentenceTokenList.add(tokenAnnotation.getCoveredText()); } Span[] names = find(cas, (String[]) sentenceTokenList.toArray(new String[sentenceTokenList.size()])); AnnotationFS nameAnnotations[] = new AnnotationFS[names.length]; for (int i = 0; i < names.length; i++) { int startIndex = ((AnnotationFS) sentenceTokenAnnotationList.get(names[i].getStart())).getBegin(); int endIndex = ((AnnotationFS) sentenceTokenAnnotationList.get(names[i].getEnd() - 1)).getEnd(); nameAnnotations[i] = cas.createAnnotation(mNameType, startIndex, endIndex); cas.getIndexRepository().addFS(nameAnnotations[i]); } postProcessAnnotations(names, nameAnnotations); } documentDone(cas); }
/** * Returns the (one) annotation of a given type that is aligned with another annotation. * * @param annotation An annotation. * @return The annotation aligned with another annotation. */ private AnnotationFS getAnnotation(Type type, AnnotationFS annotation) { List<AnnotationFS> annotations = CasUtil.selectCovered(annotation.getCAS(), type, annotation); if (annotations.size() != 1) { getLogger() .warn( "Could not find matching annotation of type " + type + " for annotation: " + annotation.getCoveredText()); return null; } return annotations.get(0); }
@Override public boolean incrementToken() throws IOException { if (iterator == null) { initializeIterator(); } if (iterator.hasNext()) { clearAttributes(); AnnotationFS next = iterator.next(); termAttr.append(next.getCoveredText()); offsetAttr.setOffset(correctOffset(next.getBegin()), correctOffset(next.getEnd())); return true; } else { return false; } }
private static Collection<String> getTokensFromAnnotation( AnnotationFS annotation, boolean useLemma, int minTokenLength) { Collection<String> tokens; if (useLemma) { tokens = new ArrayList<>(); /* concatenate multiple lemmas: */ // selectCovered(Lemma.class, annotation).stream() // .map(lemma -> lemma.getValue()) // .filter(lemma -> lemma.length() >= minTokenLength) // .reduce((l1, l2) -> l1 + "_" + l2) // .ifPresent(token -> tokens.add(token)); for (Lemma lemma : selectCovered(Lemma.class, annotation)) { String text = lemma.getValue(); if (text.length() >= minTokenLength) { tokens.add(text); } } } else { tokens = new ArrayList<>(1); String text = annotation.getCoveredText(); if (text.length() >= minTokenLength) { tokens.add(text); } } return tokens; }
/** * Creates a Stem annotation with same begin and end as the AnnotationFS fs, the value is the * stemmed value derived by applying the featurepath. * * @param jcas the JCas * @param fs the AnnotationFS where the Stem annotation is created * @throws AnalysisEngineProcessException if the {@code stem} method from the snowball stemmer * cannot be invoked. */ private void createStemAnnotation(JCas jcas, AnnotationFS fs) throws AnalysisEngineProcessException { // Check for blank text, it makes no sense to add a stem then (and raised an exception) String value = fp.getValue(fs); if (!StringUtils.isBlank(value)) { if (lowerCase) { // Fixme - should use locale/language defined in CAS. value = value.toLowerCase(Locale.US); } Stem stemAnnot = new Stem(jcas, fs.getBegin(), fs.getEnd()); SnowballProgram programm = getSnowballProgram(jcas); programm.setCurrent(value); try { // The patched snowball from Lucene has this as a method on SnowballProgram // but if we have some other snowball also in the classpath, Java might // choose to use the other. So to be safe, we use a reflection here. // -- REC, 2011-04-17 MethodUtils.invokeMethod(programm, "stem", null); } catch (Exception e) { throw new AnalysisEngineProcessException(e); } stemAnnot.setValue(programm.getCurrent()); stemAnnot.addToIndexes(jcas); // Try setting the "stem" feature on Tokens. Feature feat = fs.getType().getFeatureByBaseName("stem"); if (feat != null && feat.getRange() != null && jcas.getTypeSystem().subsumes(feat.getRange(), stemAnnot.getType())) { fs.setFeatureValue(feat, stemAnnot); } } }
@Override public void process(JCas jcas) throws AnalysisEngineProcessException { Type type = jcas.getCas().getTypeSystem().getType(TYPE_NAME); Feature entityFeature = type.getFeatureByBaseName(ENTITY_FEATURE); Feature nameFeature = type.getFeatureByBaseName(NAME_FEATURE); for (Annotation annotation : jcas.getAnnotationIndex(TokenAnnotation.type)) { String tokenPOS = ((TokenAnnotation) annotation).getPosTag(); if (NP.equals(tokenPOS) || NPS.equals(tokenPOS)) { AnnotationFS entityAnnotation = jcas.getCas().createAnnotation(type, annotation.getBegin(), annotation.getEnd()); entityAnnotation.setStringValue(entityFeature, annotation.getCoveredText()); String name = "OTHER"; // "OTHER" makes no sense. In practice, "PERSON", "COUNTRY", "E-MAIL", etc. if (annotation.getCoveredText().equals("Apache")) name = "ORGANIZATION"; entityAnnotation.setStringValue(nameFeature, name); jcas.addFsToIndexes(entityAnnotation); } } }
@Override public void process(JCas jcas) throws AnalysisEngineProcessException { // sentenceCount = 0; tokenCount = 0; String text = jcas.getDocumentText(); String[] zones = getZoneTypes(); if (isStrictZoning()) { if (zones == null || zones.length == 0) { process(jcas, text.substring(0, text.length()), 0); } else if (zones.length != 1) { throw new AnalysisEngineProcessException( new IllegalStateException("Strict zoning cannot use multiple zone types")); } else { CAS cas = jcas.getCas(); for (AnnotationFS zone : select(cas, getType(cas, zones[0]))) { int[] adjusted = limit(text, zone.getBegin(), zone.getEnd()); process(jcas, text.substring(adjusted[0], adjusted[1]), adjusted[0]); } } } else { // This set collects all zone boundaries. SortedSet<Integer> boundarySet = new TreeSet<Integer>(); boundarySet.add(0); // Add start boundary boundarySet.add(text.length()); // Add end boundary // If zoneTypes have been define then get the boundaries, otherwise we will // simply have one big zone covering the whole document. if (zones != null) { // Iterate over all the zone indices and create sentences respecting // the zone boundaries. If the zoneTypes overlap... well... bad luck! for (String zoneName : zones) { CAS cas = jcas.getCas(); for (AnnotationFS zone : select(cas, getType(cas, zoneName))) { int[] adjusted = limit(text, zone.getBegin(), zone.getEnd()); boundarySet.add(adjusted[0]); boundarySet.add(adjusted[1]); } } } // Now process all zoneTypes. There will be at least two entries in the // boundary set (see above). Iterator<Integer> bi = boundarySet.iterator(); int begin = bi.next(); while (bi.hasNext()) { int end = bi.next(); process(jcas, text.substring(begin, end), begin); begin = end; } } }
@Override public void process(JCas jcas) throws AnalysisEngineProcessException { getContext().getLogger().log(Level.CONFIG, "Entering " + this.getClass().getSimpleName()); Type tokenType = jcas.getCas().getTypeSystem().getType(Token.class.getCanonicalName()); Type stemType = jcas.getCas().getTypeSystem().getType(Stem.class.getCanonicalName()); Type lemmaType = jcas.getCas().getTypeSystem().getType(Lemma.class.getCanonicalName()); Type posType = jcas.getCas().getTypeSystem().getType(POS.class.getCanonicalName()); Type typeToRemoveType = jcas.getCas().getTypeSystem().getType(typeToRemove); if (typeToRemoveType == null) { throw new AnalysisEngineProcessException( new Throwable("Could not get type for feature path: " + typeToRemove)); } List<AnnotationFS> toRemove = new ArrayList<AnnotationFS>(); try { for (Entry<AnnotationFS, String> entry : FeaturePathFactory.select(jcas.getCas(), typeToRemove)) { AnnotationFS annotation = entry.getKey(); AnnotationFS pos; if (typeToRemoveType.equals(posType)) { pos = annotation; } else { pos = getAnnotation(posType, annotation); if (pos == null) { continue; } } String posString = pos.getType().getShortName(); if (posString.equals("ADJ") && !adj) { toRemove.add(annotation); continue; } if (posString.equals("ADV") && !adv) { toRemove.add(annotation); continue; } if (posString.equals("ART") && !art) { toRemove.add(annotation); continue; } if (posString.equals("CARD") && !card) { toRemove.add(annotation); continue; } if (posString.equals("CONJ") && !conj) { toRemove.add(annotation); continue; } if ((posString.equals("N") || posString.equals("NN") || posString.equals("NP")) && !n) { toRemove.add(annotation); continue; } if (posString.equals("O") && !o) { toRemove.add(annotation); continue; } if (posString.equals("PP") && !pp) { toRemove.add(annotation); continue; } if (posString.equals("PR") && !pr) { toRemove.add(annotation); continue; } if (posString.equals("PUNC") && !punc) { toRemove.add(annotation); continue; } if (posString.equals("V") && !v) { toRemove.add(annotation); continue; } } } catch (FeaturePathException e) { throw new AnalysisEngineProcessException(e); } for (AnnotationFS fs : toRemove) { // If we want to remove tokens, we also remove accompanying lemma, stem, POS tag. if (fs.getType().equals(tokenType)) { AnnotationFS stemFS = getAnnotation(stemType, fs); if (stemFS != null) { jcas.getCas().removeFsFromIndexes(stemFS); } AnnotationFS lemmaFS = getAnnotation(lemmaType, fs); if (lemmaFS != null) { jcas.getCas().removeFsFromIndexes(lemmaFS); } AnnotationFS posFS = getAnnotation(posType, fs); if (posFS != null) { jcas.getCas().removeFsFromIndexes(posFS); } } // We don't want to keep the feature in the token, remove it here. else { if (fs.getType().equals(stemType) || fs.getType().equals(lemmaType)) { Token token = (Token) getAnnotation(tokenType, fs); if (token != null) { String fbn = fs.getType().getShortName().toLowerCase(); Feature f = tokenType.getFeatureByBaseName(fbn); token.setFeatureValue(f, null); } } else if (fs instanceof POS) { Token token = (Token) getAnnotation(tokenType, fs); if (token != null) { token.setPos(null); } } } jcas.getCas().removeFsFromIndexes(fs); } }
/** Performs pos-tagging on the given tcas object. */ @Override public synchronized void process(CAS tcas) { final AnnotationComboIterator comboIterator = new AnnotationComboIterator(tcas, this.sentenceType, this.tokenType); for (AnnotationIteratorPair annotationIteratorPair : comboIterator) { final List<AnnotationFS> sentenceTokenAnnotationList = new LinkedList<AnnotationFS>(); final List<String> sentenceTokenList = new LinkedList<String>(); for (AnnotationFS tokenAnnotation : annotationIteratorPair.getSubIterator()) { sentenceTokenAnnotationList.add(tokenAnnotation); sentenceTokenList.add(tokenAnnotation.getCoveredText()); } final List<String> posTags = this.posTagger.tag(sentenceTokenList); double posProbabilities[] = null; if (this.probabilityFeature != null) { posProbabilities = this.posTagger.probs(); } final Iterator<String> posTagIterator = posTags.iterator(); final Iterator<AnnotationFS> sentenceTokenIterator = sentenceTokenAnnotationList.iterator(); int index = 0; while (posTagIterator.hasNext() && sentenceTokenIterator.hasNext()) { final String posTag = posTagIterator.next(); final AnnotationFS tokenAnnotation = sentenceTokenIterator.next(); tokenAnnotation.setStringValue(this.posFeature, posTag); if (posProbabilities != null) { tokenAnnotation.setDoubleValue(this.posFeature, posProbabilities[index]); } index++; } // log tokens with pos if (this.logger.isLoggable(Level.FINER)) { final StringBuilder sentenceWithPos = new StringBuilder(); sentenceWithPos.append("\""); for (final Iterator<AnnotationFS> it = sentenceTokenAnnotationList.iterator(); it.hasNext(); ) { final AnnotationFS token = it.next(); sentenceWithPos.append(token.getCoveredText()); sentenceWithPos.append('\\'); sentenceWithPos.append(token.getStringValue(this.posFeature)); sentenceWithPos.append(' '); } // delete last whitespace if (sentenceWithPos.length() > 1) // not 0 because it contains already the " char sentenceWithPos.setLength(sentenceWithPos.length() - 1); sentenceWithPos.append("\""); this.logger.log(Level.FINER, sentenceWithPos.toString()); } } }
private void create(JCas cas, Feature feature, int begin, int end, String value) { Type type = feature.getDomain(); AnnotationFS annotation = cas.getCas().createAnnotation(type, begin, end); annotation.setStringValue(feature, value); cas.addFsToIndexes(annotation); }
public boolean isIdenticalWith(AnnotationFS anno) { return anno.getBegin() == begin && anno.getEnd() == end; }
public Offsets(AnnotationFS anno) { this(anno.getBegin(), anno.getEnd()); }
private void updateCrossSentAnnoList( Map<Integer, Integer> segmentBeginEnd, Map<String, JCas> jCases, List<Type> entryTypes) { crossSentenceLists = new HashMap<>(); for (Integer begin : segmentBeginEnd.keySet()) { int thisSent = -1; Set<Integer> crossSents = new HashSet<>(); for (Type t : entryTypes) { for (JCas c : jCases.values()) { if (thisSent == -1) { thisSent = BratAjaxCasUtil.getSentenceNumber(c, begin); } // update cross-sentence annotation lists for (AnnotationFS fs : selectCovered(c.getCas(), t, this.begin, end)) { // CASE 1. annotation begins here if (fs.getBegin() >= begin && fs.getBegin() <= segmentBeginEnd.get(begin)) { if (fs.getEnd() > segmentBeginEnd.get(begin) || fs.getEnd() < begin) { Sentence s = BratAjaxCasUtil.getSentenceByAnnoEnd(c, fs.getEnd()); int thatSent = BratAjaxCasUtil.getSentenceNumber(c, s.getBegin()); crossSents.add(thatSent); } } // CASE 2. Annotation ends here else if (fs.getEnd() >= begin && fs.getEnd() <= segmentBeginEnd.get(begin)) { if (fs.getBegin() > segmentBeginEnd.get(begin) || fs.getBegin() < begin) { int thatSent = BratAjaxCasUtil.getSentenceNumber(c, fs.getBegin()); crossSents.add(thatSent); } } } for (AnnotationFS fs : selectCovered(c.getCas(), t, begin, end)) { if (fs.getBegin() <= segmentBeginEnd.get(begin) && fs.getEnd() > segmentBeginEnd.get(begin)) { Sentence s = BratAjaxCasUtil.getSentenceByAnnoEnd(c, fs.getEnd()); segmentBeginEnd.put(begin, s.getEnd()); } } } } crossSentenceLists.put(thisSent, crossSents); } }
/** Copied and modified from {@link org.apache.uima.util.CasToInlineXml} */ private static String toXML(CAS cas, AnnotationsToElements converter) throws SAXException { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); XMLSerializer sax2xml = new XMLSerializer(byteArrayOutputStream, false); // get document text String docText = cas.getDocumentText(); char[] docCharArray = docText.toCharArray(); // get iterator over annotations sorted by increasing start position and // decreasing end position FSIterator<AnnotationFS> iterator = cas.getAnnotationIndex().iterator(); // This is basically a recursive algorithm that has had the recursion // removed through the use of an explicit Stack. We iterate over the // annotations, and if an annotation contains other annotations, we // push the parent annotation on the stack, process the children, and // then come back to the parent later. List<AnnotationFS> stack = new ArrayList<AnnotationFS>(); int pos = 0; ContentHandler handler = sax2xml.getContentHandler(); handler.startDocument(); // write the start tag converter.startRootElement(handler); // now use null is a placeholder for this artificial Document annotation AnnotationFS curAnnot = null; while (iterator.isValid()) { AnnotationFS nextAnnot = iterator.get(); if (curAnnot == null || nextAnnot.getBegin() < curAnnot.getEnd()) { // nextAnnot's start point is within the span of curAnnot if (curAnnot == null || nextAnnot.getEnd() <= curAnnot.getEnd()) // crossover span check { // nextAnnot is contained within curAnnot // write text between current pos and beginning of nextAnnot try { handler.characters(docCharArray, pos, nextAnnot.getBegin() - pos); pos = nextAnnot.getBegin(); converter.startAnnotationElement(nextAnnot, handler); // push parent annotation on stack stack.add(curAnnot); // move on to next annotation curAnnot = nextAnnot; } catch (StringIndexOutOfBoundsException e) { System.err.println( "Invalid annotation range: " + nextAnnot.getBegin() + "," + nextAnnot.getEnd() + " in document of length " + docText.length()); } } iterator.moveToNext(); } else { // nextAnnot begins after curAnnot ends // write text between current pos and end of curAnnot try { handler.characters(docCharArray, pos, curAnnot.getEnd() - pos); pos = curAnnot.getEnd(); } catch (StringIndexOutOfBoundsException e) { System.err.println( "Invalid annotation range: " + curAnnot.getBegin() + "," + curAnnot.getEnd() + " in document of length " + docText.length()); } converter.endAnnotationElement(curAnnot, handler); // pop next containing annotation off stack curAnnot = stack.remove(stack.size() - 1); } } // finished writing all start tags, now finish up if (curAnnot != null) { try { handler.characters(docCharArray, pos, curAnnot.getEnd() - pos); pos = curAnnot.getEnd(); } catch (StringIndexOutOfBoundsException e) { System.err.println( "Invalid annotation range: " + curAnnot.getBegin() + "," + curAnnot.getEnd() + "in document of length " + docText.length()); } converter.endAnnotationElement(curAnnot, handler); while (!stack.isEmpty()) { curAnnot = stack.remove(stack.size() - 1); // pop if (curAnnot == null) { break; } try { handler.characters(docCharArray, pos, curAnnot.getEnd() - pos); pos = curAnnot.getEnd(); } catch (StringIndexOutOfBoundsException e) { System.err.println( "Invalid annotation range: " + curAnnot.getBegin() + "," + curAnnot.getEnd() + "in document of length " + docText.length()); } converter.endAnnotationElement(curAnnot, handler); } } if (pos < docCharArray.length) { handler.characters(docCharArray, pos, docCharArray.length - pos); } converter.endRootElement(handler); handler.endDocument(); // return XML string return new String(byteArrayOutputStream.toByteArray()); }
@Test public void testExpandOffsets() throws Exception { String html = "<Parent>\n"; html += "<Child1>Some content</Child1>\n"; html += "<Child2 attribute=“someValue” />\n"; html += "<Child3>More content.</Child3>\n"; html += "</Parent>\n"; URL urlA = HtmlAnnotator.class.getClassLoader().getResource("HtmlAnnotator.xml"); if (urlA == null) { urlA = HtmlAnnotator.class .getClassLoader() .getResource("org/apache/uima/ruta/engine/HtmlAnnotator.xml"); } URL urlC = HtmlAnnotator.class.getClassLoader().getResource("HtmlConverter.xml"); if (urlC == null) { urlC = HtmlAnnotator.class .getClassLoader() .getResource("org/apache/uima/ruta/engine/HtmlConverter.xml"); } XMLInputSource inA = new XMLInputSource(urlA); ResourceSpecifier specifierA = UIMAFramework.getXMLParser().parseResourceSpecifier(inA); AnalysisEngine aeA = UIMAFramework.produceAnalysisEngine(specifierA); aeA.setConfigParameterValue(HtmlAnnotator.PARAM_ONLY_CONTENT, false); aeA.reconfigure(); XMLInputSource inC = new XMLInputSource(urlC); ResourceSpecifier specifierC = UIMAFramework.getXMLParser().parseResourceSpecifier(inC); AnalysisEngine aeC = UIMAFramework.produceAnalysisEngine(specifierC); aeC.setConfigParameterValue(HtmlConverter.PARAM_SKIP_WHITESPACES, false); aeC.setConfigParameterValue(HtmlConverter.PARAM_PROCESS_ALL, true); aeC.setConfigParameterValue(HtmlConverter.PARAM_EXPAND_OFFSETS, true); aeC.reconfigure(); CAS cas = aeA.newCAS(); Type tagType = cas.getTypeSystem().getType(HtmlAnnotator.NAMESPACE + "TAG"); Feature expandedFeature = tagType.getFeatureByBaseName("expandedOffsets"); AnnotationIndex<AnnotationFS> ai = null; FSIterator<AnnotationFS> iterator = null; cas.setDocumentText(html); aeA.process(cas); aeC.process(cas); CAS plainTextCas = cas.getView(HtmlConverter.DEFAULT_MODIFIED_VIEW); assertEquals("Some contentMore content.", plainTextCas.getDocumentText()); ai = plainTextCas.getAnnotationIndex(tagType); iterator = ai.iterator(); assertEquals(4, ai.size()); AnnotationFS next = null; next = iterator.next(); assertEquals(false, next.getBooleanValue(expandedFeature)); assertEquals("Some contentMore content.", next.getCoveredText()); next = iterator.next(); assertEquals(false, next.getBooleanValue(expandedFeature)); assertEquals("Some content", next.getCoveredText()); next = iterator.next(); boolean b1 = next.getBooleanValue(expandedFeature); assertEquals("More content.", next.getCoveredText()); next = iterator.next(); boolean b2 = next.getBooleanValue(expandedFeature); assertEquals("More content.", next.getCoveredText()); // for one of these two annotation (with same offsets) the feature must be set to true assertEquals(true, b1 || b2); cas.release(); }
/** Get the link following the current link. */ private AnnotationFS getNextLink(AnnotationFS aLink) { return (AnnotationFS) aLink.getFeatureValue(aLink.getType().getFeatureByBaseName(linkNextFeatureName)); }
/** Set the link following the current link. */ private void setNextLink(AnnotationFS aLink, AnnotationFS aNext) { aLink.setFeatureValue(aLink.getType().getFeatureByBaseName(linkNextFeatureName), aNext); }
/** * Add annotations from the CAS, which is controlled by the window size, to the brat response * {@link GetDocumentResponse} * * @param aJcas The JCAS object containing annotations * @param aResponse A brat response containing annotations in brat protocol * @param aBratAnnotatorModel Data model for brat annotations * @param aColoringStrategy the coloring strategy to render this layer (ignored) */ @Override public void render( JCas aJcas, List<AnnotationFeature> aFeatures, GetDocumentResponse aResponse, BratAnnotatorModel aBratAnnotatorModel, ColoringStrategy aColoringStrategy) { // Get begin and end offsets of window content int windowBegin = BratAjaxCasUtil.selectByAddr( aJcas, Sentence.class, aBratAnnotatorModel.getSentenceAddress()) .getBegin(); int windowEnd = BratAjaxCasUtil.selectByAddr( aJcas, Sentence.class, BratAjaxCasUtil.getLastSentenceAddressInDisplayWindow( aJcas, aBratAnnotatorModel.getSentenceAddress(), aBratAnnotatorModel.getPreferences().getWindowSize())) .getEnd(); // Find the features for the arc and span labels - it is possible that we do not find a // feature for arc/span labels because they may have been disabled. AnnotationFeature spanLabelFeature = null; AnnotationFeature arcLabelFeature = null; for (AnnotationFeature f : aFeatures) { if (WebAnnoConst.COREFERENCE_TYPE_FEATURE.equals(f.getName())) { spanLabelFeature = f; } if (WebAnnoConst.COREFERENCE_RELATION_FEATURE.equals(f.getName())) { arcLabelFeature = f; } } // At this point arc and span feature labels must have been found! If not, the later code // will crash. Type chainType = getAnnotationType(aJcas.getCas()); Feature chainFirst = chainType.getFeatureByBaseName(chainFirstFeatureName); int colorIndex = 0; // Iterate over the chains for (FeatureStructure chainFs : selectFS(aJcas.getCas(), chainType)) { AnnotationFS linkFs = (AnnotationFS) chainFs.getFeatureValue(chainFirst); AnnotationFS prevLinkFs = null; // Every chain is supposed to have a different color String color = ColoringStrategy.PALETTE_NORMAL_FILTERED[ colorIndex % ColoringStrategy.PALETTE_NORMAL_FILTERED.length]; // The color index is updated even for chains that have no visible links in the current // window because we would like the chain color to be independent of visibility. In // particular the color of a chain should not change when switching pages/scrolling. colorIndex++; // Iterate over the links of the chain while (linkFs != null) { Feature linkNext = linkFs.getType().getFeatureByBaseName(linkNextFeatureName); AnnotationFS nextLinkFs = (AnnotationFS) linkFs.getFeatureValue(linkNext); // Is link after window? If yes, we can skip the rest of the chain if (linkFs.getBegin() >= windowEnd) { break; // Go to next chain } // Is link before window? We only need links that being within the window and that // end within the window if (!(linkFs.getBegin() >= windowBegin) && (linkFs.getEnd() <= windowEnd)) { // prevLinkFs remains null until we enter the window linkFs = nextLinkFs; continue; // Go to next link } String bratTypeName = TypeUtil.getBratTypeName(this); // Render span { String bratLabelText = TypeUtil.getBratLabelText( this, linkFs, (spanLabelFeature != null) ? asList(spanLabelFeature) : Collections.EMPTY_LIST); Offsets offsets = new Offsets(linkFs.getBegin() - windowBegin, linkFs.getEnd() - windowBegin); aResponse.addEntity( new Entity( BratAjaxCasUtil.getAddr(linkFs), bratTypeName, offsets, bratLabelText, color)); } // Render arc (we do this on prevLinkFs because then we easily know that the current // and last link are within the window ;) if (prevLinkFs != null) { String bratLabelText = null; if (linkedListBehavior && arcLabelFeature != null) { // Render arc label bratLabelText = TypeUtil.getBratLabelText(this, prevLinkFs, asList(arcLabelFeature)); } else { // Render only chain type bratLabelText = TypeUtil.getBratLabelText(this, prevLinkFs, Collections.EMPTY_LIST); } List<Argument> argumentList = asList( new Argument("Arg1", BratAjaxCasUtil.getAddr(prevLinkFs)), new Argument("Arg2", BratAjaxCasUtil.getAddr(linkFs))); aResponse.addRelation( new Relation( BratAjaxCasUtil.getAddr(prevLinkFs), bratTypeName, argumentList, bratLabelText, color)); } // if (BratAjaxCasUtil.isSame(linkFs, nextLinkFs)) { // log.error("Loop in CAS detected, aborting rendering of chains"); // break; // } prevLinkFs = linkFs; linkFs = nextLinkFs; } } }