/*
* 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 de.tudarmstadt.ukp.clarin.webanno.api.annotation.util.WebAnnoCasUtil.getAddr;
import static de.tudarmstadt.ukp.clarin.webanno.api.annotation.util.WebAnnoCasUtil.getFeature;
import static de.tudarmstadt.ukp.clarin.webanno.api.annotation.util.WebAnnoCasUtil.selectByAddr;
import static java.util.Arrays.asList;
import static org.apache.uima.fit.util.CasUtil.getType;
import static org.apache.uima.fit.util.CasUtil.selectCovered;
import static org.apache.uima.fit.util.JCasUtil.selectCovered;
import java.util.ArrayList;
import java.util.List;
import org.apache.uima.cas.ArrayFS;
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.annotation.adapter.SpanAdapter;
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.LinkWithRoleModel;
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.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;
import de.tudarmstadt.ukp.clarin.webanno.model.LinkMode;
import de.tudarmstadt.ukp.clarin.webanno.model.MultiValueMode;
import de.tudarmstadt.ukp.dkpro.core.api.segmentation.type.Sentence;
/**
* Render spans.
*/
public class BratSpanRenderer
implements TypeRenderer
{
private SpanAdapter typeAdapter;
public BratSpanRenderer(SpanAdapter 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 aBratAnnotatorModel
* Data model for brat annotations
* @param aColoringStrategy
* the coloring strategy to render this layer
*/
@Override
public void render(JCas aJcas, List<AnnotationFeature> aFeatures,
GetDocumentResponse aResponse, AnnotatorState aBratAnnotatorModel,
ColoringStrategy aColoringStrategy)
{
Type type = getType(aJcas.getCas(), typeAdapter.getAnnotationTypeName());
int windowBegin = aBratAnnotatorModel.getWindowBeginOffset();
int windowEnd = aBratAnnotatorModel.getWindowEndOffset();
List<Sentence> visibleSentences = selectCovered(aJcas, Sentence.class, windowBegin,
windowEnd);
for (AnnotationFS fs : selectCovered(aJcas.getCas(), type, windowBegin, windowEnd)) {
String bratTypeName = TypeUtil.getUiTypeName(typeAdapter);
String bratLabelText = TypeUtil.getUiLabelText(typeAdapter, fs, aFeatures);
String color = aColoringStrategy.getColor(fs, bratLabelText);
Sentence beginSent = null;
Sentence endSent = null;
// check if annotation extends beyond viewable window - if yes, then constrain it to
// the visible window
for (Sentence sentence : visibleSentences) {
if (beginSent == null) {
if (sentence.getBegin() <= fs.getBegin() && fs.getBegin() < sentence.getEnd()) {
beginSent = sentence;
}
}
if (endSent == null) {
if (sentence.getBegin() <= fs.getEnd() && fs.getEnd() <= sentence.getEnd()) {
endSent = sentence;
}
}
if (beginSent != null && endSent != null) {
break;
}
}
if (beginSent == null || endSent == null) {
throw new IllegalStateException(
"Unable to determine sentences in which the annotation starts/ends: " + fs);
}
List<Sentence> sentences = selectCovered(aJcas, Sentence.class, beginSent.getBegin(),
endSent.getEnd());
List<Offsets> offsets = new ArrayList<Offsets>();
if (sentences.size() > 1) {
for (Sentence sentence : sentences) {
if (sentence.getBegin() <= fs.getBegin() && fs.getBegin() < sentence.getEnd()) {
offsets.add(new Offsets(fs.getBegin() - windowBegin, sentence
.getEnd() - windowBegin));
}
else if (sentence.getBegin() <= fs.getEnd() && fs.getEnd() <= sentence.getEnd()) {
offsets.add(new Offsets(sentence.getBegin() - windowBegin, fs
.getEnd() - windowBegin));
}
else {
offsets.add(new Offsets(sentence.getBegin() - windowBegin,
sentence.getEnd() - windowBegin));
}
}
aResponse.addEntity(new Entity(getAddr(fs), bratTypeName, offsets, bratLabelText,
color));
}
else {
// FIXME It should be possible to remove this case and the if clause because
// the case that a FS is inside a single sentence is just a special case
aResponse.addEntity(new Entity(getAddr(fs), bratTypeName, new Offsets(fs.getBegin()
- windowBegin, fs.getEnd() - windowBegin), bratLabelText,
color));
}
// Render errors if required features are missing
renderRequiredFeatureErrors(aFeatures, fs, aResponse);
// Render slots
int fi = 0;
for (AnnotationFeature feat : typeAdapter.listFeatures()) {
if (MultiValueMode.ARRAY.equals(feat.getMultiValueMode())
&& LinkMode.WITH_ROLE.equals(feat.getLinkMode())) {
List<LinkWithRoleModel> links = getFeature(fs, feat);
ArrayFS linksFS = (ArrayFS) fs.getFeatureValue(fs.getType()
.getFeatureByBaseName(feat.getName()));
for (int li = 0; li < links.size(); li++) {
LinkWithRoleModel link = links.get(li);
FeatureStructure targetFS = selectByAddr(fs.getCAS(), link.targetAddr);
FeatureStructure linkFS = linksFS.get(li);
// get the color of the link for suggestion annotations
color = aColoringStrategy.getColor(fs + "-" + targetFS + "-" + linkFS,
bratLabelText);
aResponse.addRelation(new Relation(new VID(getAddr(fs), fi, li),
bratTypeName, getArgument(fs, targetFS), link.role, color));
}
}
fi++;
}
}
}
/**
* Argument lists for the arc annotation
*/
private List<Argument> getArgument(FeatureStructure aGovernorFs, FeatureStructure aDependentFs)
{
return asList(new Argument("Arg1", getAddr(aGovernorFs)), new Argument("Arg2",
getAddr(aDependentFs)));
}
}