/*
* 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.api.annotation.model;
import static de.tudarmstadt.ukp.clarin.webanno.api.annotation.util.WebAnnoCasUtil.getAddr;
import static de.tudarmstadt.ukp.clarin.webanno.api.annotation.util.WebAnnoCasUtil.getLastSentenceInDisplayWindow;
import static org.apache.uima.fit.util.JCasUtil.select;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.uima.cas.CASException;
import org.apache.uima.jcas.JCas;
import de.tudarmstadt.ukp.clarin.webanno.api.annotation.util.WebAnnoCasUtil;
import de.tudarmstadt.ukp.clarin.webanno.constraints.model.ParsedConstraints;
import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationFeature;
import de.tudarmstadt.ukp.clarin.webanno.model.AnnotationLayer;
import de.tudarmstadt.ukp.clarin.webanno.model.LinkMode;
import de.tudarmstadt.ukp.clarin.webanno.model.Mode;
import de.tudarmstadt.ukp.clarin.webanno.model.Project;
import de.tudarmstadt.ukp.clarin.webanno.model.ScriptDirection;
import de.tudarmstadt.ukp.clarin.webanno.model.SourceDocument;
import de.tudarmstadt.ukp.clarin.webanno.model.Tag;
import de.tudarmstadt.ukp.clarin.webanno.model.TagSet;
import de.tudarmstadt.ukp.clarin.webanno.security.model.User;
import de.tudarmstadt.ukp.dkpro.core.api.segmentation.type.Sentence;
/**
* Data model for annotation editors
*/
public class AnnotatorStateImpl
implements Serializable, AnnotatorState, TransientActionContext
{
private static final long serialVersionUID = 1078613192789450714L;
/**
* The Project the annotator working on
*/
private Project project;
/**
* The source document the to be annotated
*/
private SourceDocument document;
private int documentIndex = -1;
private int numberOfDocuments = -1;
/**
* The current user annotating the document
*/
private User user;
private ScriptDirection scriptDirection;
/**
* The sentence address where the display window starts with, in its UIMA annotation
*/
private int displayWindowStartSentenceAddress = -1;
/**
* The begin offset of a sentence
*/
private int sentenceBeginOffset;
/**
* The end offset of a sentence
*/
private int sentenceEndOffset;
/**
* The begin offset of the first visible sentence.
*/
private int windowBeginOffset;
/**
* The end offset of the last visible sentence.
*/
private int windowEndOffset;
/**
* the sentence number where an action occured (selection, modification, clicking)
*/
private int focusSentenceNumber;
/**
* The first sentence number in the display window
*/
private int firstVisibleSentenceNumber;
/**
* The last sentence number in the display window
*/
private int lastVisibleSentenceNumber;
/**
* The total number of sentences in the document
*/
private int numberOfSentences;
private List<FeatureState> featureModels = new ArrayList<>();
/**
* Constraints object from rule file
*/
private ParsedConstraints constraints;
// Annotation preferences, to be saved in a file system
/**
* The annotation layers available in the current project.
*/
private List<AnnotationLayer> annotationLayers = new ArrayList<AnnotationLayer>();
// /**
// * The number of sentences to be displayed at a time
// */
// private int windowSize = 5;
//
// /**
// * Used to enable/disable auto-scrolling while annotation
// */
// private boolean scrollPage = true;
//
// // determine if static color for annotations will be used or we shall
// // dynamically generate one
// private boolean staticColor = true;
private AnnotationPreference preferences = new AnnotationPreference();
/**
* The Mode of the current operations as either {@link Mode#ANNOTATION} or as
* {@link Mode#CURATION}
*/
private Mode mode;
/**
* The previously selected {@link TagSet} and {@link Tag} for a span/Arc annotation so as toz
* pre-fill the type in the span/arc annotation dialog (only for new span/arc annotations)
*/
private AnnotationLayer rememberedSpanLayer;
private AnnotationLayer rememberedArcLayer;
private Map<AnnotationFeature, Serializable> rememberedSpanFeatures = new HashMap<AnnotationFeature, Serializable>();
private Map<AnnotationFeature, Serializable> rememberedArcFeatures = new HashMap<AnnotationFeature, Serializable>();
// the selected annotation layer
private AnnotationLayer selectedAnnotationLayer;
// Text field to capture key-bindings for forward annoatations
private String forwardAnno;
// the default annotation layer
private AnnotationLayer defaultAnnotationLayer;
// the name of the default annotation layer
private String layerName;
// enable automatic forward annotations
private boolean forwardAnnotation;
// User action while annotating on document
private String userAction;
public AnnotatorStateImpl(Mode aMode)
{
mode = aMode;
}
@Override
public ParsedConstraints getConstraints()
{
return constraints;
}
@Override
public void setConstraints(ParsedConstraints aConstraints)
{
constraints = aConstraints;
}
@Override
public String getUserAction()
{
return userAction;
}
@Override
public void setUserAction(String aUserAction)
{
userAction = aUserAction;
}
@Override
public void clearUserAction()
{
userAction = null;
}
private final Selection selection = new Selection();
@Override
public Selection getSelection()
{
return selection;
}
@Override
public Project getProject()
{
return project;
}
@Override
public void setProject(Project aProject)
{
project = aProject;
setScriptDirection(project.getScriptDirection());
}
@Override
public ScriptDirection getScriptDirection()
{
return scriptDirection;
}
@Override
public void toggleScriptDirection()
{
if (ScriptDirection.LTR.equals(getScriptDirection())) {
setScriptDirection(ScriptDirection.RTL);
}
else {
setScriptDirection(ScriptDirection.LTR);
}
}
@Override
public void setScriptDirection(ScriptDirection aScriptDirection)
{
scriptDirection = aScriptDirection;
}
@Override
public SourceDocument getDocument()
{
return document;
}
@Override
public int getDocumentIndex()
{
return documentIndex;
}
@Override
public int getNumberOfDocuments()
{
return numberOfDocuments;
}
@Override
public void setDocument(SourceDocument aDocument, List<SourceDocument> aDocuments)
{
document = aDocument;
if (aDocument != null) {
documentIndex = aDocuments.indexOf(aDocument);
numberOfDocuments = aDocuments.size();
}
else {
documentIndex = -1;
numberOfDocuments = -1;
}
}
@Override
public User getUser()
{
return user;
}
@Override
public void setUser(User aUser)
{
user = aUser;
}
@Override
public void setFirstVisibleSentence(Sentence aSentence)
{
JCas jcas;
try {
jcas = aSentence.getCAS().getJCas();
}
catch (CASException e) {
throw new IllegalStateException("Unable to fetch JCas from CAS", e);
}
displayWindowStartSentenceAddress = WebAnnoCasUtil.getAddr(aSentence);
sentenceBeginOffset = aSentence.getBegin();
sentenceEndOffset = aSentence.getEnd();
Sentence lastVisibleSentence = getLastSentenceInDisplayWindow(jcas, getAddr(aSentence),
getPreferences().getWindowSize());
firstVisibleSentenceNumber = WebAnnoCasUtil.getSentenceNumber(jcas,
aSentence.getBegin());
lastVisibleSentenceNumber = WebAnnoCasUtil.getSentenceNumber(jcas,
lastVisibleSentence.getBegin());
numberOfSentences = select(jcas, Sentence.class).size();
windowBeginOffset = aSentence.getBegin();
windowEndOffset = lastVisibleSentence.getEnd();
}
@Override
public int getWindowBeginOffset()
{
return windowBeginOffset;
}
@Override
public int getWindowEndOffset()
{
return windowEndOffset;
}
@Override
public int getFirstVisibleSentenceAddress()
{
return displayWindowStartSentenceAddress;
}
@Override
public List<AnnotationLayer> getAnnotationLayers()
{
return annotationLayers;
}
@Override
public void setAnnotationLayers(List<AnnotationLayer> aAnnotationLayers)
{
annotationLayers = aAnnotationLayers;
}
@Override
public AnnotationPreference getPreferences()
{
return preferences;
}
@Override
public void setPreferences(AnnotationPreference aPreferences)
{
preferences = aPreferences;
}
@Override
public Mode getMode()
{
return mode;
}
@Override
public AnnotationLayer getRememberedSpanLayer()
{
return rememberedSpanLayer;
}
@Override
public AnnotationLayer getRememberedArcLayer()
{
return rememberedArcLayer;
}
@Override
public Map<AnnotationFeature, Serializable> getRememberedSpanFeatures()
{
return rememberedSpanFeatures;
}
private void setRememberedSpanFeatures(List<FeatureState> aModels)
{
rememberedSpanFeatures = new HashMap<>();
if (aModels != null) {
for (FeatureState fm : aModels) {
// Do not remember values unless this feature is enabled
if (!fm.feature.isRemember()) {
continue;
}
// Do not remember link features.
if (!LinkMode.NONE.equals(fm.feature.getLinkMode())) {
continue;
}
rememberedSpanFeatures.put(fm.feature, fm.value);
}
}
}
@Override
public Map<AnnotationFeature, Serializable> getRememberedArcFeatures()
{
return rememberedArcFeatures;
}
private void setRememberedArcFeatures(List<FeatureState> aModels)
{
rememberedArcFeatures = new HashMap<>();
if (aModels != null) {
for (FeatureState fm : aModels) {
// Do not remember values unless this feature is enabled
if (!fm.feature.isRemember()) {
continue;
}
// Do not remember link features.
if (!LinkMode.NONE.equals(fm.feature.getLinkMode())) {
continue;
}
rememberedArcFeatures.put(fm.feature, fm.value);
}
}
}
@Override
public int getFirstVisibleSentenceBegin()
{
return sentenceBeginOffset;
}
@Override
public int getFirstVisibleSentenceEnd()
{
return sentenceEndOffset;
}
@Override
public int getFocusSentenceNumber()
{
return focusSentenceNumber;
}
@Override
public void setFocusSentenceNumber(int aSentenceNumber)
{
focusSentenceNumber = aSentenceNumber;
}
@Override
public int getFirstVisibleSentenceNumber()
{
return firstVisibleSentenceNumber;
}
@Override
public int getLastVisibleSentenceNumber()
{
return lastVisibleSentenceNumber;
}
@Override
public int getNumberOfSentences()
{
return numberOfSentences;
}
@Override
public AnnotationLayer getSelectedAnnotationLayer()
{
return selectedAnnotationLayer;
}
@Override
public void setSelectedAnnotationLayer(AnnotationLayer selectedAnnotationLayer)
{
this.selectedAnnotationLayer = selectedAnnotationLayer;
}
@Override
public AnnotationLayer getDefaultAnnotationLayer()
{
return defaultAnnotationLayer;
}
@Override
public void setDefaultAnnotationLayer(AnnotationLayer defaultAnnotationLayer)
{
this.defaultAnnotationLayer = defaultAnnotationLayer;
}
@Override
public boolean isForwardAnnotation()
{
return forwardAnnotation;
}
@Override
public void setForwardAnnotation(boolean forwardAnnotation)
{
this.forwardAnnotation = forwardAnnotation;
}
@Override
public void rememberFeatures()
{
if (getSelection().isRelationAnno()) {
this.rememberedArcLayer = getSelectedAnnotationLayer();
setRememberedArcFeatures(featureModels);
}
else {
this.rememberedSpanLayer = getSelectedAnnotationLayer();
setRememberedSpanFeatures(featureModels);
}
}
@Override
public void clearRememberedFeatures()
{
setRememberedArcFeatures(null);
this.rememberedArcLayer = null;
setRememberedSpanFeatures(null);
this.rememberedSpanLayer = null;
}
@Override
public void clearAllSelections()
{
getSelection().clear();
clearArmedSlot();
}
private AnnotationFeature armedFeature;
private int armedSlot = -1;
@Override
public void setArmedSlot(AnnotationFeature aName, int aIndex)
{
armedFeature = aName;
armedSlot = aIndex;
}
@Override
public boolean isArmedSlot(AnnotationFeature aName, int aIndex)
{
return ObjectUtils.equals(aName, armedFeature) && aIndex == armedSlot;
}
@Override
public void clearArmedSlot()
{
armedFeature = null;
armedSlot = -1;
}
@Override
public boolean isSlotArmed()
{
return armedFeature != null;
}
@Override
public AnnotationFeature getArmedFeature()
{
return armedFeature;
}
@Override
public int getArmedSlot()
{
return armedSlot;
}
@Override
public List<FeatureState> getFeatureStates()
{
return featureModels;
}
@Override
public FeatureState getFeatureState(AnnotationFeature aFeature)
{
for (FeatureState f : featureModels) {
if (f.feature.getId() == aFeature.getId()) {
return f;
}
}
return null;
}
@Override
public TransientActionContext getAction()
{
return this;
}
// if it is annotation or delete operation
private boolean isAnnotate = true;
@Override
public boolean isAnnotate()
{
return isAnnotate;
}
@Override
public void setAnnotate(boolean isAnnotate)
{
this.isAnnotate = isAnnotate;
}
}