blob: 7f7f27dd14da8e881675b9259609b039cb2c64f3 [file] [log] [blame]
package org.apache.uima.casviewer.core.internal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.uima.cas.CAS;
import org.apache.uima.cas.FSIndex;
import org.apache.uima.cas.FSIterator;
import org.apache.uima.cas.FeatureStructure;
import org.apache.uima.cas.SofaFS;
import org.apache.uima.cas.Type;
import org.apache.uima.cas.TypeSystem;
import org.apache.uima.cas.text.AnnotationFS;
import org.apache.uima.resource.metadata.TypeDescription;
import org.apache.uima.resource.metadata.TypePriorities;
import org.apache.uima.resource.metadata.TypeSystemDescription;
import org.apache.uima.tools.debug.util.Trace;
import org.apache.uima.tools.internal.util.CasHelper;
/**
*
*
* Notes:
* - Each CAS view may have differently selected types
*
* - CAS defaultView = cas.getView(CAS.NAME_DEFAULT_SOFA);
*/
public class CASObjectView implements ICASObjectView
{
static final public String CASDIFF_VIEW_TYPE = "org.apache.uima.casviewer.CasDiffView";
static final public String CASDIFF_VIEW_SUPER_TYPE = "uima.cas.AnnotationBase";
static final public String CASDIFF_VIEW_FEATURE_ADDED = "addedFeatureStructures";
static final public String CASDIFF_VIEW_FEATURE_DELETED = "deletedFeatureStructures";
static final public String CASDIFF_VIEW_FEATURE_CHANGED = "changedFeatureStructures";
static final public String CASDIFF_VIEW_FEATURE_MATCHED = "matchedFeatureStructures";
private BaseCASObject baseCASObject;
private SofaFS sofa;
private String sofaId;
// private TypeSystemStyle tsStyle;
protected CAS casView;
protected TypeSystemDescription typeSystemDescription;
protected TypePriorities typePriorities;
protected TypeSystem typeSystem;
protected CasIndexRepository casIndexRepository;
// Key: type name; Value: list of annotations (AnnotationObject) for that type
private Map<String, List<AnnotationObject>> typeName2AnnotationsMap = new HashMap<String, List<AnnotationObject>>(10);
// Key: Entity; Value: list of EntityAnnotation's instances for this Entity
// private Map mapEntity2AnnotationList = new HashMap();
// List of types having annotations. These "types" are collected from AnnotationIndex.
// Therefore, this list may NOT contains all the types in the type system.
// The map "typeName2AnnotationsMap" can be used to get all the annotation for a
// particular type name.
// List of types having annotations.
private List<Type> typesHavingAnnotations;
// List of annotations indexed by the default AnnotationIndex
private LinkedList<AnnotationObject> indexedAnnotationList = new LinkedList<AnnotationObject>();
private boolean isProcessed = false; // used by processAnnotatedTypes()
// List of pre-selected types (read from type system style file)
protected Set preSelectedTypes = new HashSet();
// List of hidden types (read from type system style file)
// These types never appear in UI of CASView
protected Set hiddenTypes = new HashSet();
// List of currently selected Types for this CAS View
protected Set currentlySelectedTypes = new HashSet();
protected Set visibleTypes;
private List mHighFrequencyTypes = new ArrayList();
//Mode constants
// private static final short MODE_ANNOTATIONS = 0;
// private static final short MODE_ENTITIES = 1;
// private short mViewMode = MODE_ANNOTATIONS;
private static String[] DEFAULT_HIDDEN_FEATURES = { "sofa" };
// private boolean mConsistentColors = true;
private Set<String> mHiddenFeatureNames = new HashSet<String>();
// private Boolean mEntityViewEnabled; //null if user has made no choice
/*************************************************************************/
public CASObjectView (BaseCASObject aCASObject, SofaFS aSofaFS) {
this(aCASObject, aSofaFS, aSofaFS.getStringValue(aSofaFS.getType().getFeatureByBaseName(CAS.FEATURE_BASE_NAME_SOFAID)));
}
@Deprecated // Use CASObjectView (BaseCASObject aCASObject, SofaFS sofa)
protected CASObjectView (BaseCASObject aCASObject, SofaFS sofa, String sofaId) {
this.baseCASObject = aCASObject;
this.sofa = sofa;
this.sofaId = sofaId;
this.casView = aCASObject.getCAS().getView(sofa);
this.typeSystemDescription = aCASObject.typeSystemDescription;
this.typePriorities = aCASObject.typePriorities;
}
/*************************************************************************/
// Used by FSIndexSectionPart
static public AnnotationObject createAnnotationObjectFromFS (FeatureStructure fs, boolean show)
{
int begin;
int end;
Type type = fs.getType();
if (fs instanceof AnnotationFS) {
begin = fs.getIntValue(type.getFeatureByBaseName("begin"));
end = fs.getIntValue(type.getFeatureByBaseName("end"));
} else {
begin = 0;
end = 0;
}
return new AnnotationObject(fs, begin, end, show);
}
/*************************************************************************/
/**
* Set the list of types that occur most frequently. This method assigns
* the most distinguishable colors to these types.
*
* @param aTypeNames names of types that are occur frequently. Ideally these should be
* ordered by frequency, with the most frequent being first.
*/
public void setHighFrequencyTypes(String[] aTypeNames)
{
//store these types for later
mHighFrequencyTypes.clear();
mHighFrequencyTypes.addAll(Arrays.asList(aTypeNames));
// mTypeNameToColorMap.clear();
// assignColors(mHighFrequencyTypes);
}
/**
* Set the list of types that will be highlighted in the viewer. Types not in this
* list will not appear in the legend and will never be highlighted. If this method
* is not called, the default is to show all types in the CAS (except those specifically
* hidden by a call to {@link #setHiddenTypes(String[])}.
*
* @param aTypeNames names of types that are to be highlighted. Null indicates that
* all types in the CAS should be highlighted.
*/
public void setDisplayedTypes(String[] aDisplayedTypeNames)
{
if (aDisplayedTypeNames == null) {
visibleTypes = null;
} else {
visibleTypes = new HashSet();
visibleTypes.addAll(Arrays.asList(aDisplayedTypeNames));
}
}
/**
* Set the list of types that will NOT be highlighted in the viewer.
*
* @param aTypeNames names of types that are never to be highlighted.
*/
public void setHiddenTypes(String[] aTypeNames)
{
hiddenTypes.clear();
hiddenTypes.addAll(Arrays.asList(aTypeNames));
}
/**
* Configures the initially selected types in the viewer. If not called, all
* types will be initially selected.
*
* @param aTypeNames array of fully-qualified names of types to be initially selected
*/
public void setInitiallySelectedTypes(String[] aTypeNames)
{
preSelectedTypes = new HashSet();
for (int i = 0; i < aTypeNames.length; i++)
{
preSelectedTypes.add(aTypeNames[i]);
}
currentlySelectedTypes.addAll(preSelectedTypes);
}
/**
* Configures the viewer to hide certain features in the
* annotation deatail pane.
*
* @param aFeatureName array of (short) feature names to be hidden
*/
public void setHiddenFeatures(String[] aFeatureNames)
{
mHiddenFeatureNames.clear();
//add default hidden features
mHiddenFeatureNames.addAll(Arrays.asList(DEFAULT_HIDDEN_FEATURES));
//add user-defined hidden features
mHiddenFeatureNames.addAll(Arrays.asList(aFeatureNames));
}
/*************************************************************************/
/* (non-Javadoc)
* @see com.ibm.uima.casviewer.core.ICASObjectView#getTypeTree()
*/
public TypeTree getTypeTree ()
{
return baseCASObject.getTypeTree();
}
public TypeTree newTypeTree ()
{
return baseCASObject.newTypeTree();
}
/**
* Decorate each TypeNode of TypeTree with AnnotationObjectsNode containing:
* - AnnotationObject(s)
*
* @param typeTree
* @return void
*/
public void decorateTypeTreeWithAnnotationObjects (TypeTree typeTree) {
TypeNode typeNode;
for (Type type: getTypesHavingAnnotations()) {
List<AnnotationObject> annotationlist = getAnnotationsForTypeName(type.getName());
if ( CASDIFF_VIEW_TYPE.equals(type.getName()) ) {
Trace.err("Found " + CASDIFF_VIEW_TYPE);
} else {
typeNode = typeTree.getTypeNode(type.getName());
if (typeNode != null) {
AnnotationObjectsNode n = typeNode.getAnnotationNode();
if (n == null) {
n = new AnnotationObjectsNode(annotationlist);
typeNode.setAnnotationNode(n);
} else {
// Replace OLD annotations
n.setAnnotationList(annotationlist);
}
} else {
Trace.err("Cannot find type in tree: " + type.getName());
}
}
}
}
/**
* Visit tree from "fromNode" and add annotation objects to
* each type. The AnnotationObject(s) are newly created
* and put under a new BaseNode.
*
* fromNode
* - AnnotationListNode annotationNode
* List<AnnotationObject>
* |- AnnotationObject 1
* |- AnnotationObject 2
* |- ...
* List<AnnotationObject>
*
* @param fromNode
* @param casView
* @return void
*/
static public void visitTreeAndAddAnnotations (TypeNode fromNode, CAS casView)
{
// Get "Type" object to work with FSIndex when retrieving FS from index
String typeName = ((TypeDescription) fromNode.getObject()).getName();
Type type = casView.getTypeSystem().getType(typeName);
// Is sub-type of AnnotationBase ?
Type annotationBase = casView.getTypeSystem().getType("uima.cas.AnnotationBase");
if (!typeName.equals("uima.cas.TOP") &&
!casView.getTypeSystem().subsumes(annotationBase, type)) {
return;
}
FSIndex fsIndex = CasHelper.getIndexForType(casView, typeName);
if (!typeName.equals("uima.cas.TOP") && !typeName.equals("uima.cas.AnnotationBase")
&& (fsIndex == null || fsIndex.size() == 0)) {
// Skip Type having NO Annotation
return;
}
// Add FeatureStructures as children of fromNode
if (fsIndex != null) {
FSIterator iter = fsIndex.iterator();
List<AnnotationObject> selfAnnotations = new ArrayList<AnnotationObject>();
while (iter.isValid()) {
FeatureStructure fs = iter.get();
if (fs.getType() == type) {
// Create new object for this type's annotation
String coveredText = null;
if (fs instanceof AnnotationFS) {
coveredText = ((AnnotationFS)fs).getCoveredText();
}
selfAnnotations.add(new AnnotationObject (fs, coveredText));
}
iter.moveToNext();
}
if (selfAnnotations.size() > 0) {
AnnotationObjectsNode n = new AnnotationObjectsNode(selfAnnotations);
fromNode.setAnnotationNode(n);
}
}
// Visit Children Types
if ( fromNode.getChildren() != null ) {
// Collection c = fromNode.getChildren().values();
Iterator<TypeNode> iter = fromNode.getChildrenIterator();
while (iter.hasNext()) {
visitTreeAndAddAnnotations (iter.next(), casView);
}
}
}
/*************************************************************************/
public void setTypeSelectionByName (List types, boolean selection)
{
if ( selection ) {
currentlySelectedTypes.add(types);
} else {
if (types == null) {
currentlySelectedTypes.clear();
} else {
currentlySelectedTypes.remove(types);
}
}
}
public List<Type> getTypesHavingAnnotations ()
{
if (!isProcessed) {
processCasView();
// processEntities ();
}
// typeName2TypeMap.values();
return typesHavingAnnotations;
}
public LinkedList<AnnotationObject> getIndexedAnnotations ()
{
if (!isProcessed) {
processCasView();
}
return indexedAnnotationList;
}
public String getDocumentText() {
if (casView == null) {
return "";
}
return casView.getDocumentText();
}
public Type getTypeFromTypeSystem (String typeName) {
return getTypeSystem().getType(typeName);
}
public List<AnnotationObject> getAnnotationsForTypeName(String typeName) {
return typeName2AnnotationsMap.get(typeName);
}
protected void preProcessCasView () {
}
protected AnnotationObject createAnnotationObject(FeatureStructure fs,
int begin, int end, String doc, int maxTextSpan) {
AnnotationObject newAnnot = new AnnotationObject (fs, begin, end);
newAnnot.setTextSpan(CasHelper.getAnnotationTextSpan(doc, (AnnotationFS) fs, maxTextSpan));
return newAnnot;
}
private void processCasView ()
{
if (isProcessed) return;
isProcessed = true;
if (typesHavingAnnotations == null) {
typesHavingAnnotations = new ArrayList<Type> ();
} else {
typesHavingAnnotations.clear();
}
// Clear previous annotations
indexedAnnotationList.clear();
String doc = casView.getDocumentText();
if (doc == null) {
doc = "";
}
preProcessCasView ();
// Iterate over the default annotation index.
// Create a list "annotListPerType" of annotations for EACH type
List<AnnotationObject> annotListPerType = null;// For each type, list of its annotations
FSIterator iter = casView.getAnnotationIndex().iterator(); // Get the default annotation index.
while (iter.isValid())
{
// Get 1 annotation
AnnotationFS fs = (AnnotationFS) iter.get();
iter.moveToNext();
// Get the type of this annotation
Type type = fs.getType();
// PrintAnnotations.printFS((FeatureStructure) fs, mTCAS, 1, System.out);
int begin = fs.getBegin(); // start from 0
int end = fs.getEnd();
// if (begin != end && end == mTCAS.getDocumentText().length()) {
// end--;
// }
if (begin == end) {
// Skip 0-length annotations
// E.g., SourceDocumentInformation
continue;
}
// See this Type before ?
if ( ! typesHavingAnnotations.contains(type) ) {
// New Type for Annotations
// Check that this type should be displayed.
if ((visibleTypes != null && ! typeNamesContains(visibleTypes, type.getName()))
|| typeNamesContains(hiddenTypes, type.getName())) {
continue;
}
typesHavingAnnotations.add(type);
// Map type name -> Type
// typeName2TypeMap.put(type.getName(), type);
// Map type name -> annotation list
annotListPerType = new ArrayList<AnnotationObject>();
typeName2AnnotationsMap.put(type.getName(), annotListPerType);
} else {
// Existing Type. Get existing annotation list
annotListPerType = typeName2AnnotationsMap.get(type.getName());
}
// Select color for the type
// if (tsStyle != null) {
// typeStyle = tsStyle.getTypeStyle(type.getName());
// }
// Create new object for this type's annotation
// Try to reuse AnnotationObject created at somewhere
AnnotationObject newAnnot = createAnnotationObject(fs, begin, end, doc, 50);
annotListPerType.add(newAnnot);
// Sort Annotations (sortedAnnotationList) by BEGIN feature
// AnnotationObject annot;
// boolean done = false;
// Trace.trace("sortedAnnotationList:" + sortedAnnotationList.size());
indexedAnnotationList.add(newAnnot);
// Sorted ? Use order set by AnnotationIndexRepository
// addAnnotationToSortedList(indexedAnnotationList, newAnnot);
// if (type.getName().compareTo("uima.tcas.DocumentAnnotation") != 0 && begin != end) {
// // Trace.trace( type.getName() + "Begin: " + begin + " ; end: " + end);
// StyleRange styleRange = new StyleRange ();
// styleRange.foreground = styledTextDoc.getDisplay().getSystemColor(SWT.COLOR_BLUE);
// styleRange.start = begin;
// styleRange.length= end - begin;
// styledTextDoc.setStyleRange(styleRange);
// }
}
// for (int i=0; i<indexedAnnotationList.size(); ++i) {
// Annot annot = ((Annot) indexedAnnotationList.get(i));
// Trace.trace("annot: " + annot.type.getShortName() + "["
// + annot.begin + " , " + annot.end + "]");
// }
// Trace.trace("annotatedTypes.size(): " + annotatedTypes.size());
// Trace.trace("indexedAnnotationList.size(): " + indexedAnnotationList.size());
} // processAnnotatedTypes
/**
* Does wildcard matching to determine if a give type name is "contained" in
* a Set of type names.
*
* @param names
* Type names, which may include wildcards (e.g, uima.tt.*)
* @param name
* A type name
* @return True iff name matches a name in type names
*/
private boolean typeNamesContains(Set<String> names, String name)
{
if (names.contains(name))
return true;
else
{
Iterator<String> namesIterator = names.iterator();
while (namesIterator.hasNext())
{
String otherName = (String) namesIterator.next();
if (otherName.indexOf('*') != -1) {
if (wildCardMatch(name, otherName)){
return true;
}
} else {
if (otherName.equalsIgnoreCase(name))
{
return true;
}
}
}
}
return false;
}
/**
* Helper for {@link #typeNamesContains(HashSet, String)}.
*
* @param s
* A litteral string
* @param pattern
* A string that includes one or more *'s as wildcards
* @return True iff the string matches the pattern.
*/
private boolean wildCardMatch(String s, String pattern)
{
StringBuffer regexpPatternBuffer = new StringBuffer();
for (int i = 0; i < pattern.length(); i++)
{
char c = pattern.charAt(i);
if (c == '*')
regexpPatternBuffer.append('.');
else if (c == '.')
regexpPatternBuffer.append('\\');
if (Character.isLetter(c))
{
regexpPatternBuffer.append('(').append(Character.toLowerCase(c)).append('|').append(
Character.toUpperCase(c)).append(')');
}
else
{
regexpPatternBuffer.append(c);
}
}
return s.matches(new String(regexpPatternBuffer));
}
protected void addAnnotationToSortedList (LinkedList sortedAnnotationList, AnnotationObject newAnnot)
{
AnnotationObject annot;
int begin = newAnnot.begin;
int end = newAnnot.end;
boolean done = false;
for (int i=0; i<sortedAnnotationList.size(); ++i) {
// Trace.trace("" + i);
annot = (AnnotationObject) sortedAnnotationList.get(i);
int s = annot.begin;
int e = annot.end;
if (e <= begin) {
// begin<------>end
// s<---->e
continue;
} else if (e <= end) {
if (s > begin) {
// begin<-------------->end
// s<-------->e
sortedAnnotationList.add(i, newAnnot);
Trace.bug("Insert annot " + newAnnot.type.getName() + "(begin:" + newAnnot.begin + ")" + " at " + i);
done = true;
break;
} else {
// s <= begin
// begin<-------------->end
// s<----------->e
continue; // goto next
}
} else {
// e > end
// begin<------>end
// .......-------->e
if (s <= begin) {
// begin<------>end
// s<------------>e
continue;
} else {
// s > begin
// begin<------>end
// s<--------->e
sortedAnnotationList.add(i, newAnnot);
Trace.bug("Insert annot " + newAnnot.type.getName() + "(begin:" + newAnnot.begin + ")" + " at " + i);
done = true;
break;
}
}
}
if (!done) {
if (sortedAnnotationList.size() > 0) {
sortedAnnotationList.addLast(newAnnot);
// Trace.trace("NEW annot is inserted at LAST");
} else {
// 1st element
sortedAnnotationList.addFirst(newAnnot);
// Trace.trace("NEW annot is inserted at FIRST");
}
// } else {
// Trace.trace("NEW annot at " + (indexAnnotation-1) + " is inserted at: " + i);
}
}
/*************************************************************************/
public CasIndexRepository getCasIndexRepository () {
if (casIndexRepository == null) {
casIndexRepository = CasIndexRepository.createInstance(getCurrentView());
}
return casIndexRepository;
}
public TypeSystem getTypeSystem () {
if (typeSystem == null) {
typeSystem = baseCASObject.getTypeSystem();
}
return typeSystem;
}
public TypeSystemDescription getTypeSystemDescription() {
return typeSystemDescription;
}
public TypePriorities getTypePriorities() {
return typePriorities;
}
public SofaFS getSofa() {
return sofa;
}
public String getSofaId() {
return sofaId;
}
public CAS getCurrentView() {
return casView;
}
public Type getTypeByName(String typeName) {
return typeSystem.getType(typeName);
}
/**
* @return the baseCASObject
*/
public BaseCASObject getBaseCASObject() {
return baseCASObject;
}
}