/* * Copyright 2012 * Ubiquitous Knowledge Processing (UKP) Lab and FG Language Technology * Technische Universität Darmstadt * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package de.tudarmstadt.ukp.clarin.webanno.brat.adapter; import static java.util.Arrays.asList; import static org.apache.uima.fit.util.CasUtil.selectFS; import java.util.Collections; import java.util.List; import org.apache.uima.cas.Feature; import org.apache.uima.cas.FeatureStructure; import org.apache.uima.cas.Type; import org.apache.uima.cas.text.AnnotationFS; import org.apache.uima.jcas.JCas; import de.tudarmstadt.ukp.clarin.webanno.api.WebAnnoConst; import de.tudarmstadt.ukp.clarin.webanno.api.annotation.adapter.ChainAdapter; import de.tudarmstadt.ukp.clarin.webanno.api.annotation.coloring.ColoringStrategy; import de.tudarmstadt.ukp.clarin.webanno.api.annotation.model.AnnotatorState; import de.tudarmstadt.ukp.clarin.webanno.api.annotation.model.VID; import de.tudarmstadt.ukp.clarin.webanno.api.annotation.util.TypeUtil; import de.tudarmstadt.ukp.clarin.webanno.api.annotation.util.WebAnnoCasUtil; import de.tudarmstadt.ukp.clarin.webanno.brat.message.GetDocumentResponse; import de.tudarmstadt.ukp.clarin.webanno.brat.render.model.Argument; import de.tudarmstadt.ukp.clarin.webanno.brat.render.model.Entity; import de.tudarmstadt.ukp.clarin.webanno.brat.render.model.Offsets; import de.tudarmstadt.ukp.clarin.webanno.brat.render.model.Relation; import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationFeature; /** * Render */ public class BratChainRenderer implements TypeRenderer { private ChainAdapter typeAdapter; public BratChainRenderer(ChainAdapter aTypeAdapter) { typeAdapter = aTypeAdapter; } /** * 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 aState * 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, AnnotatorState aState, ColoringStrategy aColoringStrategy) { // 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 = typeAdapter.getAnnotationType(aJcas.getCas()); Feature chainFirst = chainType.getFeatureByBaseName(typeAdapter.getChainFirstFeatureName()); 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(typeAdapter.getLinkNextFeatureName()); AnnotationFS nextLinkFs = (AnnotationFS) linkFs.getFeatureValue(linkNext); // Is link after window? If yes, we can skip the rest of the chain if (linkFs.getBegin() >= aState.getWindowEndOffset()) { 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() >= aState.getWindowBeginOffset()) && (linkFs.getEnd() <= aState.getWindowEndOffset())) { // prevLinkFs remains null until we enter the window linkFs = nextLinkFs; continue; // Go to next link } String bratTypeName = TypeUtil.getUiTypeName(typeAdapter); // Render span { String bratLabelText = TypeUtil.getUiLabelText(typeAdapter, linkFs, (spanLabelFeature != null) ? asList(spanLabelFeature) : Collections.EMPTY_LIST); Offsets offsets = new Offsets(linkFs.getBegin() - aState.getWindowBeginOffset(), linkFs.getEnd() - aState.getWindowBeginOffset()); VID vid = new VID(WebAnnoCasUtil.getAddr(linkFs), VID.NONE, VID.NONE, VID.NONE); aResponse.addEntity( new Entity(vid, 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 (typeAdapter.isLinkedListBehavior() && arcLabelFeature != null) { // Render arc label bratLabelText = TypeUtil.getUiLabelText(typeAdapter, prevLinkFs, asList(arcLabelFeature)); } else { // Render only chain type bratLabelText = TypeUtil.getUiLabelText(typeAdapter, prevLinkFs, Collections.EMPTY_LIST); } List<Argument> argumentList = asList( new Argument("Arg1", WebAnnoCasUtil.getAddr(prevLinkFs)), new Argument("Arg2", WebAnnoCasUtil.getAddr(linkFs))); VID vid = new VID(WebAnnoCasUtil.getAddr(prevLinkFs), 1, VID.NONE, VID.NONE); aResponse.addRelation( new Relation(vid, bratTypeName, argumentList, bratLabelText, color)); } // Render errors if required features are missing renderRequiredFeatureErrors(aFeatures, linkFs, aResponse); // if (BratAjaxCasUtil.isSame(linkFs, nextLinkFs)) { // log.error("Loop in CAS detected, aborting rendering of chains"); // break; // } prevLinkFs = linkFs; linkFs = nextLinkFs; } } } }