/* | |
* Licensed to the Apache Software Foundation (ASF) under one | |
* or more contributor license agreements. See the NOTICE file | |
* distributed with this work for additional information | |
* regarding copyright ownership. The ASF licenses this file | |
* to you 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 org.apache.uima.ruta; | |
import static java.util.Arrays.asList; | |
import static java.util.Collections.emptySet; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.Collections; | |
import java.util.HashSet; | |
import java.util.LinkedHashSet; | |
import java.util.LinkedList; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Map.Entry; | |
import java.util.NavigableMap; | |
import java.util.NoSuchElementException; | |
import java.util.Set; | |
import java.util.TreeMap; | |
import org.apache.commons.collections.CollectionUtils; | |
import org.apache.commons.lang3.StringUtils; | |
import org.apache.uima.cas.BooleanArrayFS; | |
import org.apache.uima.cas.CAS; | |
import org.apache.uima.cas.CASException; | |
import org.apache.uima.cas.ConstraintFactory; | |
import org.apache.uima.cas.DoubleArrayFS; | |
import org.apache.uima.cas.FSIndex; | |
import org.apache.uima.cas.FSIterator; | |
import org.apache.uima.cas.FSMatchConstraint; | |
import org.apache.uima.cas.Feature; | |
import org.apache.uima.cas.FeatureStructure; | |
import org.apache.uima.cas.FloatArrayFS; | |
import org.apache.uima.cas.IntArrayFS; | |
import org.apache.uima.cas.StringArrayFS; | |
import org.apache.uima.cas.Type; | |
import org.apache.uima.cas.TypeSystem; | |
import org.apache.uima.cas.impl.FSIteratorImplBase; | |
import org.apache.uima.cas.impl.TypeImpl; | |
import org.apache.uima.cas.text.AnnotationFS; | |
import org.apache.uima.cas.text.AnnotationIndex; | |
import org.apache.uima.fit.util.CasUtil; | |
import org.apache.uima.fit.util.FSCollectionFactory; | |
import org.apache.uima.jcas.JCas; | |
import org.apache.uima.jcas.tcas.Annotation; | |
import org.apache.uima.ruta.block.RutaBlock; | |
import org.apache.uima.ruta.engine.RutaEngine; | |
import org.apache.uima.ruta.expression.AnnotationTypeExpression; | |
import org.apache.uima.ruta.expression.IRutaExpression; | |
import org.apache.uima.ruta.expression.annotation.IAnnotationExpression; | |
import org.apache.uima.ruta.expression.annotation.IAnnotationListExpression; | |
import org.apache.uima.ruta.expression.bool.IBooleanExpression; | |
import org.apache.uima.ruta.expression.bool.IBooleanListExpression; | |
import org.apache.uima.ruta.expression.feature.CoveredTextFeature; | |
import org.apache.uima.ruta.expression.feature.FeatureExpression; | |
import org.apache.uima.ruta.expression.feature.FeatureMatchExpression; | |
import org.apache.uima.ruta.expression.feature.GenericFeatureExpression; | |
import org.apache.uima.ruta.expression.feature.LazyFeature; | |
import org.apache.uima.ruta.expression.feature.SimpleFeatureExpression; | |
import org.apache.uima.ruta.expression.feature.TypeFeature; | |
import org.apache.uima.ruta.expression.number.INumberExpression; | |
import org.apache.uima.ruta.expression.number.INumberListExpression; | |
import org.apache.uima.ruta.expression.string.IStringExpression; | |
import org.apache.uima.ruta.expression.string.IStringListExpression; | |
import org.apache.uima.ruta.expression.type.ITypeExpression; | |
import org.apache.uima.ruta.expression.type.ITypeListExpression; | |
import org.apache.uima.ruta.rule.AbstractRule; | |
import org.apache.uima.ruta.rule.AbstractRuleMatch; | |
import org.apache.uima.ruta.rule.MatchContext; | |
import org.apache.uima.ruta.type.RutaAnnotation; | |
import org.apache.uima.ruta.type.RutaBasic; | |
import org.apache.uima.ruta.type.RutaOptional; | |
import org.apache.uima.ruta.utils.RutaListUtils; | |
import org.apache.uima.ruta.utils.UIMAUtils; | |
import org.apache.uima.ruta.visitor.InferenceCrowd; | |
public class RutaStream extends FSIteratorImplBase<AnnotationFS> { | |
private final CAS cas; | |
private FSIterator<AnnotationFS> basicIt; | |
private FSIterator<AnnotationFS> currentIt; | |
private AnnotationFS documentAnnotation; | |
private Type documentAnnotationType; | |
private Type basicType; | |
private NavigableMap<Integer, RutaBasic> beginAnchors = new TreeMap<>(); | |
private NavigableMap<Integer, RutaBasic> endAnchors = new TreeMap<>(); | |
private FilterManager filter; | |
private boolean dynamicAnchoring; | |
private double indexPenalty = 0; | |
private double anchoringFactor; | |
private boolean lowMemoryProfile; | |
private boolean simpleGreedyForComposed; | |
private InferenceCrowd crowd; | |
private TypeUsageInformation typeUsage; | |
private Boolean greedyRuleElement; | |
private Boolean greedyRule; | |
private boolean onlyOnce = false; | |
private Annotation documentBeginAnchor; | |
private Annotation documentEndAnchor; | |
private boolean emptyIsInvisible; | |
private long maxRuleMatches; | |
private long maxRuleElementMatches; | |
public RutaStream(CAS cas, Type basicType, FilterManager filter, boolean lowMemoryProfile, | |
boolean simpleGreedyForComposed, boolean emptyIsInvisible, TypeUsageInformation typeUsage, | |
InferenceCrowd crowd) { | |
super(); | |
this.cas = cas; | |
this.filter = filter; | |
this.basicType = basicType; | |
this.lowMemoryProfile = lowMemoryProfile; | |
this.simpleGreedyForComposed = simpleGreedyForComposed; | |
this.emptyIsInvisible = emptyIsInvisible; | |
this.typeUsage = typeUsage; | |
this.crowd = crowd; | |
AnnotationFS additionalWindow = filter.getWindowAnnotation(); | |
updateIterators(cas, basicType, filter, additionalWindow); | |
// really an if? sub it of basic should fix this | |
if (additionalWindow == null) { | |
documentAnnotation = cas.getDocumentAnnotation(); | |
documentAnnotationType = getCas().getDocumentAnnotation().getType(); | |
basicIt.moveToFirst(); | |
documentBeginAnchor = new RutaOptional(getJCas(), 0, 0); | |
documentEndAnchor = new RutaOptional(getJCas(), documentAnnotation.getEnd(), | |
documentAnnotation.getEnd()); | |
} else { | |
documentAnnotation = additionalWindow; | |
documentAnnotationType = filter.getWindowType(); | |
} | |
} | |
protected RutaStream(CAS cas, Type basicType, NavigableMap<Integer, RutaBasic> beginAnchors, | |
NavigableMap<Integer, RutaBasic> endAnchors, FilterManager filter, | |
boolean lowMemoryProfile, boolean simpleGreedyForComposed, boolean emptyIsInvisible, | |
TypeUsageInformation typeUsage, InferenceCrowd crowd) { | |
super(); | |
this.cas = cas; | |
this.beginAnchors = beginAnchors; | |
this.endAnchors = endAnchors; | |
this.filter = filter; | |
this.basicType = basicType; | |
this.lowMemoryProfile = lowMemoryProfile; | |
this.simpleGreedyForComposed = simpleGreedyForComposed; | |
this.emptyIsInvisible = emptyIsInvisible; | |
this.typeUsage = typeUsage; | |
this.crowd = crowd; | |
AnnotationFS additionalWindow = filter.getWindowAnnotation(); | |
updateIterators(cas, basicType, filter, additionalWindow); | |
// really an if? sub it of basic should fix this | |
if (additionalWindow == null) { | |
documentAnnotation = cas.getDocumentAnnotation(); | |
documentAnnotationType = getCas().getDocumentAnnotation().getType(); | |
basicIt.moveToFirst(); | |
} else { | |
documentAnnotation = additionalWindow; | |
documentAnnotationType = filter.getWindowType(); | |
} | |
} | |
private void updateIterators(AnnotationFS additionalWindow) { | |
updateIterators(cas, basicType, filter, additionalWindow); | |
} | |
private void updateIterators(CAS cas, Type basicType, FilterManager filter, | |
AnnotationFS additionalWindow) { | |
if (additionalWindow != null) { | |
basicIt = cas.getAnnotationIndex(basicType).subiterator(additionalWindow); | |
} else { | |
basicIt = cas.getAnnotationIndex(basicType).iterator(); | |
} | |
currentIt = filter.createFilteredIterator(cas, basicType); | |
} | |
@Deprecated | |
public void initalizeBasics(String[] reindexOnly, boolean reindexOnlyMentionedTypes) { | |
RutaIndexingConfiguration config = new RutaIndexingConfiguration(); | |
config.setReindexOnly(reindexOnly); | |
config.setReindexOnlyMentionedTypes(reindexOnlyMentionedTypes); | |
config.setReindexUpdateMode(ReindexUpdateMode.ADDITIVE); | |
initalizeBasics(config); | |
} | |
public void initalizeBasics(RutaIndexingConfiguration config) { | |
AnnotationIndex<AnnotationFS> basicIndex = cas.getAnnotationIndex(basicType); | |
boolean basicsAvailable = basicIndex.size() != 0; | |
if (config.getReindexUpdateMode() == ReindexUpdateMode.NONE && basicsAvailable) { | |
// there are already some ruta basics and we do not want to update anything, since we know we | |
// do not need to. Only set internal maps | |
initializeInternalAnchorMaps(basicIndex); | |
return; | |
} | |
if (!basicsAvailable) { | |
// indexing | |
createBasics(config); | |
} else { | |
// reindexing | |
updateBasics(basicIndex, config); | |
} | |
} | |
private void createBasics(RutaIndexingConfiguration config) { | |
TypeSystem typeSystem = cas.getTypeSystem(); | |
Collection<Type> indexTypes; | |
if (config.isIndexOnlyMentionedTypes()) { | |
indexTypes = convertNamesToTypes(typeUsage.getUsedTypes().toArray(new String[0]), typeSystem); | |
} else { | |
indexTypes = convertNamesToTypes(config.getIndexOnly(), typeSystem); | |
} | |
Collection<Type> indexSkipTypes = convertNamesToTypes(config.getIndexSkipTypes(), typeSystem); | |
Collection<Type> indexParentTypes = removeSubsumedTypes(indexTypes, typeSystem); | |
Collection<Type> allIndexTypes = expandToAllSubtypes(indexTypes, indexSkipTypes, typeSystem); | |
List<FSIndex<AnnotationFS>> relevantIndexes = getRelevantIndexes(typeSystem, indexParentTypes, | |
indexSkipTypes); | |
createBasics(relevantIndexes, allIndexTypes); | |
} | |
private void updateBasics(AnnotationIndex<AnnotationFS> basicIndex, | |
RutaIndexingConfiguration config) { | |
TypeSystem typeSystem = cas.getTypeSystem(); | |
Collection<Type> reindexTypes; | |
if (config.isReindexOnlyMentionedTypes()) { | |
reindexTypes = convertNamesToTypes(typeUsage.getUsedTypes().toArray(new String[0]), | |
typeSystem); | |
} else { | |
reindexTypes = convertNamesToTypes(config.getReindexOnly(), typeSystem); | |
} | |
Collection<Type> reindexSkipTypes = convertNamesToTypes(config.getReindexSkipTypes(), | |
typeSystem); | |
Collection<Type> reindexParentTypes = removeSubsumedTypes(reindexTypes, typeSystem); | |
Collection<Type> allReindexTypes = expandToAllSubtypes(reindexTypes, reindexSkipTypes, | |
typeSystem); | |
List<FSIndex<AnnotationFS>> relevantIndexes = getRelevantIndexes(typeSystem, reindexParentTypes, | |
reindexSkipTypes); | |
updateBasics(basicIndex, relevantIndexes, allReindexTypes, config.getReindexUpdateMode()); | |
} | |
private List<FSIndex<AnnotationFS>> getRelevantIndexes(TypeSystem typeSystem, | |
Collection<Type> rootReindexTypeList, Collection<Type> skipTypeList) { | |
List<FSIndex<AnnotationFS>> relevantIndexes = new ArrayList<>(); | |
for (Type type : rootReindexTypeList) { | |
if (skipTypeForIndexing(type, skipTypeList, typeSystem)) { | |
continue; | |
} | |
if (StringUtils.equals(type.getName(), CAS.TYPE_NAME_ANNOTATION)) { | |
relevantIndexes.add(cas.getAnnotationIndex().withSnapshotIterators()); | |
} else { | |
relevantIndexes.add(cas.getAnnotationIndex(type).withSnapshotIterators()); | |
} | |
} | |
return relevantIndexes; | |
} | |
private boolean skipTypeForIndexing(Type type, Collection<Type> skipTypeList, | |
TypeSystem typeSystem) { | |
// collect no ruta basics | |
if (typeSystem.subsumes(basicType, type)) { | |
return true; | |
} | |
if (skipTypeList != null) { | |
if (skipTypeList.contains(type)) { | |
return true; | |
} else { | |
for (Type skipType : skipTypeList) { | |
if (typeSystem.subsumes(skipType, type)) { | |
return true; | |
} | |
} | |
} | |
} | |
return false; | |
} | |
private void createBasics(List<FSIndex<AnnotationFS>> relevantIndexes, | |
Collection<Type> allIndexTypes) { | |
List<Integer> anchors = getSortedUniqueAnchors(relevantIndexes); | |
createBasicsForAnchors(anchors); | |
// add all annotations | |
for (FSIndex<AnnotationFS> index : relevantIndexes) { | |
for (AnnotationFS a : index) { | |
// consider skipped types | |
if (allIndexTypes == null || allIndexTypes.contains(a.getType())) { | |
addAnnotation(a, false, false, null); | |
} | |
} | |
} | |
updateIterators(documentAnnotation); | |
} | |
private void createBasicsForAnchors(List<Integer> anchors) { | |
if (anchors.size() == 0) { | |
// empty document | |
createRutaBasic(0, 0); | |
} else if (anchors.size() == 1) { | |
Integer first = anchors.get(0); | |
createRutaBasic(first, first); | |
} else { | |
for (int i = 0; i < anchors.size() - 1; i++) { | |
Integer first = anchors.get(i); | |
Integer second = anchors.get(i + 1); | |
if (first < second) { // not really needed | |
createRutaBasic(first, second); | |
} | |
} | |
} | |
} | |
private List<Integer> getSortedUniqueAnchors(List<FSIndex<AnnotationFS>> relevantIndexes) { | |
Set<Integer> anchorSet = new HashSet<>(); | |
for (FSIndex<AnnotationFS> annotationIndex : relevantIndexes) { | |
for (AnnotationFS a : annotationIndex) { | |
anchorSet.add(a.getBegin()); | |
anchorSet.add(a.getEnd()); | |
} | |
} | |
List<Integer> anchors = new ArrayList<>(anchorSet); | |
Collections.sort(anchors); | |
return anchors; | |
} | |
private void updateBasics(AnnotationIndex<AnnotationFS> basicIndex, | |
List<FSIndex<AnnotationFS>> relevantIndexes, Collection<Type> allReindexTypes, | |
ReindexUpdateMode indexUpdateMode) { | |
initializeInternalAnchorMaps(basicIndex); | |
updateRutaBasicMemoryProfile(basicIndex); | |
switch (indexUpdateMode) { | |
case COMPLETE: | |
updateBasicsComplete(basicIndex, relevantIndexes, allReindexTypes); | |
break; | |
case ADDITIVE: | |
updateBasicsAdditive(basicIndex, relevantIndexes); | |
break; | |
case SAFE_ADDITIVE: | |
updateBasicsSafeAdditive(basicIndex, relevantIndexes, allReindexTypes); | |
break; | |
case NONE: | |
// do nothing | |
break; | |
default: | |
throw new IllegalArgumentException( | |
"The given IndexUpdateMode is not supported: " + indexUpdateMode); | |
} | |
} | |
private void updateBasicsComplete(AnnotationIndex<AnnotationFS> basicIndex, | |
List<FSIndex<AnnotationFS>> relevantIndexes, Collection<Type> completeReindexTypeList) { | |
// cleanup index info for given types | |
for (AnnotationFS each : basicIndex) { | |
RutaBasic rutaBasic = (RutaBasic) each; | |
for (Type type : completeReindexTypeList) { | |
int code = ((TypeImpl) type).getCode(); | |
rutaBasic.getPartOf()[code] = 0; | |
rutaBasic.getBeginMap()[code] = null; | |
rutaBasic.getEndMap()[code] = null; | |
} | |
} | |
// add all annotations | |
for (FSIndex<AnnotationFS> index : relevantIndexes) { | |
for (AnnotationFS a : index) { | |
// consider skipped types | |
if (completeReindexTypeList.contains(a.getType())) { | |
addAnnotation(a, false, false, null); | |
} | |
} | |
} | |
} | |
private void updateBasicsAdditive(AnnotationIndex<AnnotationFS> basicIndex, | |
List<FSIndex<AnnotationFS>> relevantIndexes) { | |
// adds annotation only if not already known and included | |
for (FSIndex<AnnotationFS> annotationIndex : relevantIndexes) { | |
for (AnnotationFS a : annotationIndex) { | |
Type type = a.getType(); | |
RutaBasic beginAnchor = getBeginAnchor(a.getBegin()); | |
RutaBasic endAnchor = getEndAnchor(a.getEnd()); | |
boolean shouldBeAdded = false; | |
if (beginAnchor == null || endAnchor == null) { | |
shouldBeAdded = true; | |
} else { | |
Collection<AnnotationFS> set = beginAnchor.getBeginAnchors(type); | |
if (!set.contains(a)) { | |
shouldBeAdded = true; | |
} | |
} | |
if (shouldBeAdded) { | |
addAnnotation(a, false, false, null); | |
} | |
} | |
} | |
} | |
private void updateBasicsSafeAdditive(AnnotationIndex<AnnotationFS> basicIndex, | |
List<FSIndex<AnnotationFS>> relevantIndexes, Collection<Type> completeReindexTypeList) { | |
// search for removed annotations, and remove them | |
for (AnnotationFS each : basicIndex) { | |
RutaBasic rutaBasic = (RutaBasic) each; | |
for (Type type : completeReindexTypeList) { | |
// it's sufficient to check begin anchors, end should be consistent | |
Collection<AnnotationFS> beginAnchors = rutaBasic.getBeginAnchors(type); | |
Collection<AnnotationFS> toRemove = new ArrayList<>(); | |
for (AnnotationFS annotationAtAnchor : beginAnchors) { | |
if (!annotationAtAnchor.getCAS().getAnnotationIndex().contains(annotationAtAnchor)) { | |
// not in index? -> was removed | |
toRemove.add(annotationAtAnchor); | |
} | |
} | |
toRemove.forEach(a -> removeAnnotation(a)); | |
} | |
} | |
updateBasicsAdditive(basicIndex, relevantIndexes); | |
} | |
private void initializeInternalAnchorMaps(AnnotationIndex<AnnotationFS> basicIndex) { | |
for (AnnotationFS e : basicIndex) { | |
beginAnchors.put(e.getBegin(), (RutaBasic) e); | |
endAnchors.put(e.getEnd(), (RutaBasic) e); | |
} | |
} | |
private void updateRutaBasicMemoryProfile(AnnotationIndex<AnnotationFS> basicIndex) { | |
RutaBasic firstBasic = (RutaBasic) basicIndex.iterator().get(); | |
if (firstBasic.isLowMemoryProfile() != lowMemoryProfile) { | |
for (AnnotationFS each : basicIndex) { | |
RutaBasic eachBasic = (RutaBasic) each; | |
eachBasic.setLowMemoryProfile(lowMemoryProfile); | |
} | |
} | |
} | |
private RutaBasic createRutaBasic(int begin, int end) { | |
RutaBasic newTMB = new RutaBasic(getJCas(), begin, end); | |
newTMB.setLowMemoryProfile(lowMemoryProfile); | |
beginAnchors.put(begin, newTMB); | |
endAnchors.put(end, newTMB); | |
cas.addFsToIndexes(newTMB); | |
return newTMB; | |
} | |
public void addAnnotation(AnnotationFS annotation, boolean addToIndex, | |
AbstractRuleMatch<? extends AbstractRule> creator) { | |
addAnnotation(annotation, addToIndex, true, creator); | |
} | |
public void addAnnotation(AnnotationFS annotation, | |
AbstractRuleMatch<? extends AbstractRule> creator) { | |
addAnnotation(annotation, false, true, creator); | |
} | |
public void addAnnotation(AnnotationFS annotation, boolean addToIndex, boolean updateInternal, | |
AbstractRuleMatch<? extends AbstractRule> creator) { | |
Type type = annotation.getType(); | |
// no internal indexing for basics themselves or for zero-length annotations, exception for | |
// DocumentAnnotation | |
if (type.equals(basicType) || (annotation.getBegin() >= annotation.getEnd() | |
&& !annotation.equals(cas.getDocumentAnnotation()))) { | |
return; | |
} | |
if (indexType(annotation.getType())) { | |
boolean modified = checkSpan(annotation); | |
if (modified && updateInternal) { | |
updateIterators(filter.getWindowAnnotation()); | |
} | |
RutaBasic beginAnchor = getBeginAnchor(annotation.getBegin()); | |
RutaBasic endAnchor = getEndAnchor(annotation.getEnd()); | |
if (beginAnchor != null) { | |
beginAnchor.addBegin(annotation, type); | |
} | |
if (endAnchor != null) { | |
endAnchor.addEnd(annotation, type); | |
} | |
Collection<RutaBasic> basicAnnotationsInWindow = getAllBasicsInWindow(annotation); | |
for (RutaBasic basic : basicAnnotationsInWindow) { | |
basic.addPartOf(type); | |
} | |
} | |
if (addToIndex) { | |
cas.addFsToIndexes(annotation); | |
} | |
crowd.annotationAdded(annotation, creator); | |
} | |
private boolean indexType(Type type) { | |
if (typeUsage != null) { | |
boolean contains = typeUsage.getUsedTypesWithSubTypes().contains(type.getName()); | |
return contains; | |
} | |
return true; | |
} | |
private boolean checkSpan(AnnotationFS annotation) { | |
boolean result = false; | |
int begin = annotation.getBegin(); | |
int end = annotation.getEnd(); | |
RutaBasic beginAnchor = getBeginAnchor(begin); | |
RutaBasic endAnchor = getEndAnchor(end); | |
if (beginAnchor != null && endAnchor != null) { | |
result = false; | |
} else { | |
if (beginAnchor == null) { | |
result |= checkAnchor(begin); | |
} | |
if (endAnchor == null) { | |
result |= checkAnchor(end); | |
} | |
} | |
return result; | |
} | |
private boolean checkAnchor(int anchor) { | |
Entry<Integer, RutaBasic> floorEntry = endAnchors.floorEntry(anchor); | |
if (floorEntry == null) { | |
floorEntry = beginAnchors.floorEntry(anchor); | |
} | |
Entry<Integer, RutaBasic> ceilingEntry = endAnchors.ceilingEntry(anchor); | |
if (floorEntry != null && ceilingEntry != null) { | |
RutaBasic floor = floorEntry.getValue(); | |
RutaBasic ceiling = ceilingEntry.getValue(); | |
RutaBasic toSplit = null; | |
if (floor.getEnd() > anchor) { | |
toSplit = floor; | |
} else { | |
toSplit = ceiling; | |
} | |
int newEnd = toSplit.getEnd(); | |
if (newEnd == anchor) { | |
return false; | |
} | |
cas.removeFsFromIndexes(toSplit); | |
toSplit.setEnd(anchor); | |
RutaBasic newRB = new RutaBasic(getJCas(), anchor, newEnd); | |
newRB.setLowMemoryProfile(lowMemoryProfile); | |
newRB.setEndMap(toSplit.getEndMap().clone()); | |
newRB.setPartOf(toSplit.getPartOf().clone()); | |
toSplit.clearEndMap(); | |
cas.addFsToIndexes(toSplit); | |
cas.addFsToIndexes(newRB); | |
beginAnchors.put(floor.getBegin(), floor); | |
beginAnchors.put(newRB.getBegin(), newRB); | |
beginAnchors.put(ceiling.getBegin(), ceiling); | |
endAnchors.put(floor.getEnd(), floor); | |
endAnchors.put(newRB.getEnd(), newRB); | |
endAnchors.put(ceiling.getEnd(), ceiling); | |
return true; | |
} else { | |
// TODO this should never happen! test it! | |
} | |
return false; | |
} | |
public void removeAnnotation(AnnotationFS annotationFS) { | |
removeAnnotation(annotationFS, annotationFS.getType()); | |
} | |
public void removeAnnotation(AnnotationFS annotation, Type type) { | |
if (type.getName().equals(CAS.TYPE_NAME_DOCUMENT_ANNOTATION)) { | |
// do not remove DocumentAnnotation | |
return; | |
} | |
Collection<RutaBasic> basicAnnotationsInWindow = getAllBasicsInWindow(annotation); | |
for (RutaBasic basic : basicAnnotationsInWindow) { | |
basic.removePartOf(type); | |
} | |
Type parent = type; | |
RutaBasic beginAnchor = getBeginAnchor(annotation.getBegin()); | |
RutaBasic endAnchor = getEndAnchor(annotation.getEnd()); | |
if (beginAnchor != null) { | |
beginAnchor.removeBegin(annotation, parent); | |
} | |
if (endAnchor != null) { | |
endAnchor.removeEnd(annotation, parent); | |
} | |
if (!(annotation instanceof RutaBasic)) { | |
cas.removeFsFromIndexes(annotation); | |
} | |
} | |
public FSIterator<AnnotationFS> getFilteredBasicIterator(FSMatchConstraint constraint) { | |
ConstraintFactory cf = cas.getConstraintFactory(); | |
FSMatchConstraint matchConstraint = cf.and(constraint, filter.getDefaultConstraint()); | |
return cas.createFilteredIterator(basicIt, matchConstraint); | |
} | |
public RutaStream getWindowStream(AnnotationFS windowAnnotation, Type windowType) { | |
if (windowAnnotation.getBegin() == documentAnnotation.getBegin() | |
&& windowAnnotation.getEnd() == documentAnnotation.getEnd()) { | |
return this; | |
} | |
FilterManager filterManager = new FilterManager(filter.getDefaultFilterTypes(), | |
filter.getCurrentFilterTypes(), filter.getCurrentRetainTypes(), windowAnnotation, | |
windowType, emptyIsInvisible, cas); | |
// NavigableMap<Integer, RutaBasic> newBeginAnchors = beginAnchors.subMap( | |
// windowAnnotation.getBegin(), true, windowAnnotation.getEnd(), false); | |
// NavigableMap<Integer, RutaBasic> newEndAnchors = | |
// endAnchors.subMap(windowAnnotation.getBegin(), | |
// false, windowAnnotation.getEnd(), true); | |
RutaStream stream = new RutaStream(cas, basicType, beginAnchors, endAnchors, filterManager, | |
lowMemoryProfile, simpleGreedyForComposed, emptyIsInvisible, typeUsage, crowd); | |
stream.setDynamicAnchoring(dynamicAnchoring); | |
stream.setGreedyRuleElement(greedyRuleElement); | |
stream.setGreedyRule(greedyRule); | |
stream.setMaxRuleMatches(maxRuleMatches); | |
stream.setMaxRuleElementMatches(maxRuleElementMatches); | |
return stream; | |
} | |
@Override | |
public FSIterator<AnnotationFS> copy() { | |
RutaStream stream = new RutaStream(cas, basicType, beginAnchors, endAnchors, filter, | |
lowMemoryProfile, simpleGreedyForComposed, emptyIsInvisible, typeUsage, crowd); | |
stream.setDynamicAnchoring(dynamicAnchoring); | |
stream.setGreedyRuleElement(greedyRuleElement); | |
stream.setGreedyRule(greedyRule); | |
stream.setMaxRuleMatches(maxRuleMatches); | |
stream.setMaxRuleElementMatches(maxRuleElementMatches); | |
return stream; | |
} | |
@Override | |
public AnnotationFS get() throws NoSuchElementException { | |
return currentIt.get(); | |
} | |
@Override | |
public boolean isValid() { | |
return currentIt.isValid(); | |
} | |
@Override | |
public void moveTo(FeatureStructure fs) { | |
try { | |
currentIt.moveTo(fs); | |
} catch (Exception e) { | |
// e.printStackTrace(); | |
} | |
} | |
@Override | |
public void moveToFirst() { | |
currentIt.moveToFirst(); | |
} | |
@Override | |
public void moveToLast() { | |
currentIt.moveToLast(); | |
} | |
@Override | |
public void moveToNext() { | |
currentIt.moveToNext(); | |
} | |
@Override | |
public void moveToPrevious() { | |
currentIt.moveToPrevious(); | |
} | |
public List<AnnotationFS> getOverappingAnnotations(AnnotationFS window, Type type) { | |
List<AnnotationFS> result = new ArrayList<>(); | |
AnnotationFS newWindow = cas.createAnnotation(type, window.getBegin(), window.getEnd() - 1); | |
FSIterator<AnnotationFS> iterator = cas.getAnnotationIndex(type).iterator(newWindow); | |
if (!iterator.isValid()) { | |
iterator.moveToLast(); | |
} | |
while (iterator.isValid()) { | |
FeatureStructure fs = iterator.get(); | |
if (fs instanceof AnnotationFS) { | |
AnnotationFS a = (AnnotationFS) fs; | |
if (a.getEnd() >= window.getEnd() && a.getBegin() <= window.getBegin()) { | |
result.add(a); | |
} | |
} | |
iterator.moveToPrevious(); | |
} | |
return result; | |
} | |
public List<Annotation> getAnnotationsFollowing(Annotation annotation) { | |
List<Annotation> result = new ArrayList<>(); | |
moveTo(annotation); | |
while (currentIt.isValid()) { | |
currentIt.moveToNext(); | |
if (currentIt.isValid()) { | |
Annotation nextAnnotation = (Annotation) currentIt.get(); | |
if (nextAnnotation.getBegin() == annotation.getEnd()) { | |
result.add(nextAnnotation); | |
} else if (nextAnnotation.getBegin() >= annotation.getEnd()) { | |
break; | |
} | |
} | |
} | |
return result; | |
} | |
public CAS getCas() { | |
return cas; | |
} | |
public JCas getJCas() { | |
try { | |
return cas.getJCas(); | |
} catch (CASException e) { | |
// e.printStackTrace(); | |
} | |
return null; | |
} | |
public List<AnnotationFS> getAllofType(Type type) { | |
List<AnnotationFS> result = new ArrayList<>(); | |
FSIterator<AnnotationFS> iterator = cas.getAnnotationIndex(type).iterator(); | |
while (iterator.isValid()) { | |
FeatureStructure featureStructure = iterator.get(); | |
result.add((AnnotationFS) featureStructure); | |
iterator.moveToNext(); | |
} | |
return result; | |
} | |
public List<AnnotationFS> getAnnotationsInWindow(AnnotationFS windowAnnotation, Type type) { | |
if (windowAnnotation == null || type == null) { | |
return Collections.emptyList(); | |
} | |
TypeSystem typeSystem = getCas().getTypeSystem(); | |
List<AnnotationFS> result = new ArrayList<>(); | |
if (typeSystem.subsumes(type, windowAnnotation.getType())) { | |
result.add(windowAnnotation); | |
} | |
result.addAll(CasUtil.selectCovered(cas, type, windowAnnotation)); | |
return result; | |
} | |
public Collection<RutaBasic> getAllBasicsInWindow(AnnotationFS windowAnnotation) { | |
if (windowAnnotation.getBegin() >= windowAnnotation.getEnd()) { | |
return emptySet(); | |
} | |
RutaBasic beginAnchor = getBeginAnchor(windowAnnotation.getBegin()); | |
if (beginAnchor != null && beginAnchor.getEnd() == windowAnnotation.getEnd()) { | |
return asList(beginAnchor); | |
} | |
if (windowAnnotation.getEnd() == cas.getDocumentAnnotation().getEnd() | |
&& windowAnnotation.getBegin() == 0) { | |
return beginAnchors.values(); | |
} | |
return beginAnchors.subMap(windowAnnotation.getBegin(), true, windowAnnotation.getEnd(), false) | |
.values(); | |
} | |
public RutaBasic getBasicNextTo(boolean before, AnnotationFS annotation) { | |
if (annotation == null) { | |
return null; | |
} | |
if (before) { | |
RutaBasic pointer = endAnchors.get(annotation.getBegin()); | |
while (pointer != null && pointer.getBegin() >= documentAnnotation.getBegin()) { | |
if (isVisible(pointer)) { | |
return pointer; | |
} | |
Entry<Integer, RutaBasic> lowerEntry = endAnchors.lowerEntry(pointer.getEnd()); | |
if (lowerEntry != null) { | |
pointer = lowerEntry.getValue(); | |
} else { | |
pointer = null; | |
} | |
} | |
} else { | |
RutaBasic pointer = beginAnchors.get(annotation.getEnd()); | |
while (pointer != null && pointer.getEnd() <= documentAnnotation.getEnd()) { | |
if (isVisible(pointer)) { | |
return pointer; | |
} | |
Entry<Integer, RutaBasic> higherEntry = beginAnchors.higherEntry(pointer.getBegin()); | |
if (higherEntry != null) { | |
pointer = higherEntry.getValue(); | |
} else { | |
pointer = null; | |
} | |
} | |
} | |
return null; | |
} | |
public List<RutaBasic> getBasicsInWindow(AnnotationFS windowAnnotation) { | |
List<RutaBasic> result = new ArrayList<>(); | |
if (windowAnnotation instanceof RutaBasic) { | |
result.add((RutaBasic) windowAnnotation); | |
return result; | |
} | |
FSMatchConstraint defaultConstraint = filter.getDefaultConstraint(); | |
FSIterator<AnnotationFS> iterator = cas.createFilteredIterator( | |
cas.getAnnotationIndex(basicType).subiterator(windowAnnotation), defaultConstraint); | |
while (iterator.isValid()) { | |
result.add((RutaBasic) iterator.get()); | |
iterator.moveToNext(); | |
} | |
return result; | |
} | |
public RutaBasic getFirstBasicInWindow(AnnotationFS windowAnnotation) { | |
return getFirstBasicInWindow(windowAnnotation, currentIt); | |
} | |
public RutaBasic getFirstBasicInWindow(AnnotationFS windowAnnotation, | |
FSIterator<AnnotationFS> it) { | |
if (windowAnnotation instanceof RutaBasic) { | |
return (RutaBasic) windowAnnotation; | |
} | |
it.moveTo(windowAnnotation); | |
if (it.isValid()) { | |
return (RutaBasic) it.get(); | |
} | |
return null; | |
} | |
public List<RutaBasic> getAnnotationsOverlappingWindow(AnnotationFS annotation) { | |
if (annotation != null) { | |
return getBasicsInWindow(annotation); | |
} else { | |
return new ArrayList<>(); | |
} | |
} | |
public FSIterator<AnnotationFS> getUnfilteredBasicIterator() { | |
return basicIt; | |
} | |
public AnnotationFS getDocumentAnnotation() { | |
return documentAnnotation; | |
} | |
public void retainTypes(List<Type> list) { | |
filter.retainTypes(list); | |
currentIt = filter.createFilteredIterator(cas, basicType); | |
} | |
public void filterTypes(List<Type> list) { | |
filter.filterTypes(list); | |
currentIt = filter.createFilteredIterator(cas, basicType); | |
} | |
public void addFilterTypes(List<Type> types) { | |
filter.addFilterTypes(types); | |
currentIt = filter.createFilteredIterator(cas, basicType); | |
} | |
public void addRetainTypes(List<Type> types) { | |
filter.addRetainTypes(types); | |
currentIt = filter.createFilteredIterator(cas, basicType); | |
} | |
public void removeFilterTypes(List<Type> types) { | |
filter.removeFilterTypes(types); | |
currentIt = filter.createFilteredIterator(cas, basicType); | |
} | |
public void removeRetainTypes(List<Type> types) { | |
filter.removeRetainTypes(types); | |
currentIt = filter.createFilteredIterator(cas, basicType); | |
} | |
public FilterManager getFilter() { | |
return filter; | |
} | |
public RutaBasic getFirstBasicOfAll() { | |
if (beginAnchors.isEmpty()) { | |
return null; | |
} | |
return beginAnchors.firstEntry().getValue(); | |
} | |
public RutaBasic getLastBasicOfAll() { | |
if (endAnchors.isEmpty()) { | |
return null; | |
} | |
return endAnchors.lastEntry().getValue(); | |
} | |
public Type getDocumentAnnotationType() { | |
return documentAnnotationType; | |
} | |
public RutaBasic getNextBasic2(AnnotationFS previous) { | |
AnnotationFS pointer = cas.createAnnotation(basicType, previous.getEnd() - 1, | |
previous.getEnd()); | |
currentIt.moveTo(pointer); | |
if (currentIt.isValid()) { | |
RutaBasic basic = (RutaBasic) currentIt.get(); | |
return basic; | |
} | |
return null; | |
} | |
public long getHistogram(Type type) { | |
return cas.getAnnotationIndex(type).size(); | |
} | |
public double getIndexPenalty() { | |
return indexPenalty; | |
} | |
public RutaBasic getEndAnchor(int end) { | |
return endAnchors.get(end); | |
} | |
public RutaBasic getBeginAnchor(int begin) { | |
return beginAnchors.get(begin); | |
} | |
public boolean isDynamicAnchoring() { | |
return dynamicAnchoring; | |
} | |
public void setDynamicAnchoring(boolean dynamicAnchoring) { | |
this.dynamicAnchoring = dynamicAnchoring; | |
} | |
public boolean isGreedyRuleElement() { | |
return greedyRuleElement; | |
} | |
public void setGreedyRuleElement(Boolean greedyAnchoring) { | |
greedyRuleElement = greedyAnchoring; | |
} | |
public boolean isGreedyRule() { | |
return greedyRule; | |
} | |
public void setGreedyRule(Boolean greedyAnchoring) { | |
greedyRule = greedyAnchoring; | |
} | |
public void setIndexPenalty(double indexPenalty) { | |
this.indexPenalty = indexPenalty; | |
} | |
public double getAnchoringFactor() { | |
return anchoringFactor; | |
} | |
public void setAnchoringFactor(double anchoringFactor) { | |
this.anchoringFactor = anchoringFactor; | |
} | |
public boolean isSimpleGreedyForComposed() { | |
return simpleGreedyForComposed; | |
} | |
public void setSimpleGreedyForComposed(boolean simpleGreedyForComposed) { | |
this.simpleGreedyForComposed = simpleGreedyForComposed; | |
} | |
public boolean isGreedyAnchoring() { | |
return greedyRule || greedyRuleElement; | |
} | |
public boolean isOnlyOnce() { | |
return onlyOnce; | |
} | |
public void setOnlyOnce(Boolean onlyOnce) { | |
this.onlyOnce = onlyOnce; | |
} | |
public boolean isVisible(AnnotationFS annotationFS) { | |
return isVisible(annotationFS, false); | |
} | |
public boolean isVisible(AnnotationFS annotationFS, boolean ignoreWindow) { | |
if (annotationFS == null) { | |
return false; | |
} | |
if (annotationFS.getBegin() >= annotationFS.getEnd()) { | |
return false; | |
} | |
AnnotationFS windowAnnotation = filter.getWindowAnnotation(); | |
if (!ignoreWindow && windowAnnotation != null | |
&& (annotationFS.getBegin() < windowAnnotation.getBegin() | |
|| annotationFS.getEnd() > windowAnnotation.getEnd())) { | |
return false; | |
} | |
int begin = annotationFS.getBegin(); | |
int end = annotationFS.getEnd(); | |
Set<Type> currentHiddenTypes = filter.getCurrentHiddenTypes(); | |
RutaBasic beginAnchor = getBeginAnchor(begin); | |
if (beginAnchor != null) { | |
for (Type type : currentHiddenTypes) { | |
boolean partOf = beginAnchor.isPartOf(type); | |
if (partOf) { | |
return false; | |
} | |
} | |
} | |
RutaBasic endAnchor = getEndAnchor(end); | |
if (endAnchor != null) { | |
for (Type type : currentHiddenTypes) { | |
boolean partOf = endAnchor.isPartOf(type); | |
if (partOf) { | |
return false; | |
} | |
} | |
} | |
if (beginAnchor == null && endAnchor == null) { | |
// missing segmentation in RutaBasics maybe because of temp annotation? | |
Entry<Integer, RutaBasic> floorEntry = beginAnchors.floorEntry(Integer.valueOf(begin)); | |
Entry<Integer, RutaBasic> ceilingEntry = endAnchors.ceilingEntry(Integer.valueOf(end)); | |
if (floorEntry != null && ceilingEntry != null) { | |
for (Type type : currentHiddenTypes) { | |
if (floorEntry.getValue().isPartOf(type) || ceilingEntry.getValue().isPartOf(type)) { | |
return false; | |
} | |
} | |
} | |
} | |
return true; | |
} | |
public RutaBasic getAnchor(boolean direction, int pointer) { | |
if (direction) { | |
return getBeginAnchor(pointer); | |
} else { | |
return getEndAnchor(pointer); | |
} | |
} | |
public RutaBasic getAnchor(boolean direction, AnnotationFS annotation) { | |
if (direction) { | |
return getEndAnchor(annotation.getEnd()); | |
} else { | |
return getBeginAnchor(annotation.getBegin()); | |
} | |
} | |
public Collection<AnnotationFS> getAnnotations(Type type) { | |
Collection<AnnotationFS> result = new LinkedList<>(); | |
AnnotationFS windowAnnotation = filter.getWindowAnnotation(); | |
if (windowAnnotation != null | |
&& (windowAnnotation.getBegin() != cas.getDocumentAnnotation().getBegin() | |
|| windowAnnotation.getEnd() != cas.getDocumentAnnotation().getEnd())) { | |
AnnotationFS frame = cas.createAnnotation(cas.getTypeSystem().getType(RutaEngine.FRAME_TYPE), | |
windowAnnotation.getBegin(), windowAnnotation.getEnd()); | |
FSIterator<AnnotationFS> subiterator = cas.getAnnotationIndex(type).subiterator(frame); | |
while (subiterator.hasNext()) { | |
AnnotationFS each = subiterator.next(); | |
if (isVisible(each)) { | |
result.add(each); | |
} | |
} | |
} else { | |
AnnotationIndex<AnnotationFS> annotationIndex = cas.getAnnotationIndex(type); | |
for (AnnotationFS each : annotationIndex) { | |
if (isVisible(each)) { | |
result.add(each); | |
} | |
} | |
} | |
return result; | |
} | |
public String getVisibleCoveredText(AnnotationFS annotationFS) { | |
StringBuilder result = new StringBuilder(); | |
List<RutaBasic> basicsInWindow = getBasicsInWindow(annotationFS); | |
for (RutaBasic each : basicsInWindow) { | |
if (isVisible(each)) { | |
result.append(each.getCoveredText()); | |
} | |
} | |
return result.toString(); | |
} | |
public void assignFeatureValues(FeatureStructure annotation, | |
Map<IStringExpression, IRutaExpression> map, MatchContext context) { | |
Type type = annotation.getType(); | |
Set<Entry<IStringExpression, IRutaExpression>> entrySet = map.entrySet(); | |
for (Entry<IStringExpression, IRutaExpression> entry : entrySet) { | |
IStringExpression key = entry.getKey(); | |
IRutaExpression value = entry.getValue(); | |
String featureName = key.getStringValue(context, this); | |
Feature feature = type.getFeatureByBaseName(featureName); | |
if (feature == null) { | |
throw new IllegalArgumentException("Not able to assign feature value for feature '" | |
+ featureName + "'. Feature is not defined for type '" + type.getName() + "'" | |
+ " in script " + context.getParent().getName()); | |
} | |
assignFeatureValue(annotation, feature, value, context); | |
} | |
} | |
public void assignFeatureValue(FeatureStructure annotation, Feature feature, | |
IRutaExpression value, MatchContext context) { | |
if (feature == null || feature instanceof CoveredTextFeature | |
|| feature instanceof TypeFeature) { | |
throw new IllegalArgumentException( | |
"Not able to assign feature value (e.g., coveredText, type) in script " | |
+ context.getParent().getName()); | |
} | |
if (feature instanceof LazyFeature) { | |
LazyFeature lazyFeature = (LazyFeature) feature; | |
feature = lazyFeature.initialize(annotation); | |
} | |
CAS cas = annotation.getCAS(); | |
TypeSystem typeSystem = cas.getTypeSystem(); | |
Type range = feature.getRange(); | |
String rangeName = range.getName(); | |
if (typeSystem.subsumes(typeSystem.getType(CAS.TYPE_NAME_STRING), range)) { | |
if (value instanceof IStringExpression) { | |
IStringExpression stringExpr = (IStringExpression) value; | |
String string = stringExpr.getStringValue(context, this); | |
annotation.setStringValue(feature, string); | |
} | |
} else if (rangeName.equals(CAS.TYPE_NAME_STRING_ARRAY)) { | |
if (value instanceof IStringListExpression) { | |
IStringListExpression stringListExpr = (IStringListExpression) value; | |
List<String> stringList = stringListExpr.getStringList(context, this); | |
StringArrayFS stringArray = FSCollectionFactory.createStringArray(cas, stringList); | |
annotation.setFeatureValue(feature, stringArray); | |
} else if (value instanceof IStringExpression) { | |
IStringExpression stringExpr = (IStringExpression) value; | |
String string = stringExpr.getStringValue(context, this); | |
StringArrayFS array = FSCollectionFactory.createStringArray(cas, new String[] { string }); | |
annotation.setFeatureValue(feature, array); | |
} | |
} else if (rangeName.equals(CAS.TYPE_NAME_INTEGER) || rangeName.equals(CAS.TYPE_NAME_LONG) | |
|| rangeName.equals(CAS.TYPE_NAME_SHORT) || rangeName.equals(CAS.TYPE_NAME_BYTE)) { | |
if (value instanceof INumberExpression) { | |
INumberExpression numberExpr = (INumberExpression) value; | |
int v = numberExpr.getIntegerValue(context, this); | |
if (annotation instanceof AnnotationFS | |
&& StringUtils.equals(feature.getShortName(), CAS.FEATURE_BASE_NAME_BEGIN)) { | |
changeBegin((AnnotationFS) annotation, v, context.getRuleMatch()); | |
} else if (annotation instanceof AnnotationFS | |
&& StringUtils.equals(feature.getShortName(), CAS.FEATURE_BASE_NAME_END)) { | |
changeEnd((AnnotationFS) annotation, v, context.getRuleMatch()); | |
} else { | |
annotation.setIntValue(feature, v); | |
} | |
} | |
} else if (rangeName.equals(CAS.TYPE_NAME_INTEGER_ARRAY)) { | |
if (value instanceof INumberExpression) { | |
INumberExpression numberExpr = (INumberExpression) value; | |
int v = numberExpr.getIntegerValue(context, this); | |
IntArrayFS array = FSCollectionFactory.createIntArray(cas, new int[] { v }); | |
annotation.setFeatureValue(feature, array); | |
} else if (value instanceof INumberListExpression) { | |
INumberListExpression expr = (INumberListExpression) value; | |
List<Number> list = expr.getNumberList(context, this); | |
IntArrayFS array = FSCollectionFactory.createIntArray(cas, RutaListUtils.toIntArray(list)); | |
annotation.setFeatureValue(feature, array); | |
} | |
} else if (rangeName.equals(CAS.TYPE_NAME_DOUBLE)) { | |
if (value instanceof INumberExpression) { | |
INumberExpression numberExpr = (INumberExpression) value; | |
double v = numberExpr.getDoubleValue(context, this); | |
annotation.setDoubleValue(feature, v); | |
} | |
} else if (rangeName.equals(CAS.TYPE_NAME_DOUBLE_ARRAY)) { | |
if (value instanceof INumberExpression) { | |
INumberExpression numberExpr = (INumberExpression) value; | |
double v = numberExpr.getDoubleValue(context, this); | |
DoubleArrayFS array = FSCollectionFactory.createDoubleArray(cas, new double[] { v }); | |
annotation.setFeatureValue(feature, array); | |
} else if (value instanceof INumberListExpression) { | |
INumberListExpression expr = (INumberListExpression) value; | |
List<Number> list = expr.getNumberList(context, this); | |
DoubleArrayFS array = FSCollectionFactory.createDoubleArray(cas, | |
RutaListUtils.toDoubleArray(list)); | |
annotation.setFeatureValue(feature, array); | |
} | |
} else if (rangeName.equals(CAS.TYPE_NAME_FLOAT)) { | |
if (value instanceof INumberExpression) { | |
INumberExpression numberExpr = (INumberExpression) value; | |
float v = numberExpr.getFloatValue(context, this); | |
annotation.setFloatValue(feature, v); | |
} | |
} else if (rangeName.equals(CAS.TYPE_NAME_FLOAT_ARRAY)) { | |
if (value instanceof INumberExpression) { | |
INumberExpression numberExpr = (INumberExpression) value; | |
float v = numberExpr.getFloatValue(context, this); | |
FloatArrayFS array = FSCollectionFactory.createFloatArray(cas, new float[] { v }); | |
annotation.setFeatureValue(feature, array); | |
} else if (value instanceof INumberListExpression) { | |
INumberListExpression expr = (INumberListExpression) value; | |
List<Number> list = expr.getNumberList(context, this); | |
FloatArrayFS array = FSCollectionFactory.createFloatArray(cas, | |
RutaListUtils.toFloatArray(list)); | |
annotation.setFeatureValue(feature, array); | |
} | |
} else if (rangeName.equals(CAS.TYPE_NAME_BOOLEAN)) { | |
if (value instanceof IBooleanExpression) { | |
IBooleanExpression expr = (IBooleanExpression) value; | |
Boolean v = expr.getBooleanValue(context, this); | |
annotation.setBooleanValue(feature, v); | |
} | |
} else if (rangeName.equals(CAS.TYPE_NAME_BOOLEAN_ARRAY)) { | |
if (value instanceof IBooleanListExpression) { | |
IBooleanListExpression expr = (IBooleanListExpression) value; | |
List<Boolean> list = expr.getBooleanList(context, this); | |
BooleanArrayFS array = FSCollectionFactory.createBooleanArray(cas, list); | |
annotation.setFeatureValue(feature, array); | |
} else if (value instanceof IBooleanExpression) { | |
IBooleanExpression expr = (IBooleanExpression) value; | |
Boolean v = expr.getBooleanValue(context, this); | |
BooleanArrayFS array = FSCollectionFactory.createBooleanArray(cas, new boolean[] { v }); | |
annotation.setFeatureValue(feature, array); | |
} | |
} else if (value instanceof AnnotationTypeExpression && !range.isPrimitive()) { | |
AnnotationTypeExpression ate = (AnnotationTypeExpression) value; | |
if (range.isArray()) { | |
List<AnnotationFS> annotations = ate.getAnnotationList(context, this); | |
annotation.setFeatureValue(feature, UIMAUtils.toFSArray(getJCas(), annotations)); | |
} else { | |
AnnotationFS a = ate.getAnnotation(context, this); | |
annotation.setFeatureValue(feature, a); | |
} | |
} else if (value instanceof IAnnotationExpression && !range.isPrimitive()) { | |
IAnnotationExpression ae = (IAnnotationExpression) value; | |
boolean rangeSubsumesAnnotation = cas.getTypeSystem().subsumes(cas.getAnnotationType(), | |
range); | |
FeatureStructure a = null; | |
if (rangeSubsumesAnnotation) { | |
a = ae.getAnnotation(context, this); | |
} else { | |
a = ae.getFeatureStructure(context, this); | |
} | |
if (range.isArray()) { | |
List<FeatureStructure> c = new ArrayList<>(); | |
c.add(a); | |
annotation.setFeatureValue(feature, UIMAUtils.toFSArray(getJCas(), c)); | |
} else { | |
annotation.setFeatureValue(feature, a); | |
} | |
} else if (value instanceof IAnnotationListExpression && !range.isPrimitive()) { | |
IAnnotationListExpression ale = (IAnnotationListExpression) value; | |
List<AnnotationFS> annotations = ale.getAnnotationList(context, this); | |
if (annotations != null) { | |
if (range.isArray()) { | |
annotation.setFeatureValue(feature, UIMAUtils.toFSArray(getJCas(), annotations)); | |
} else { | |
if (annotations.isEmpty()) { | |
annotation.setFeatureValue(feature, null); | |
} else { | |
annotation.setFeatureValue(feature, annotations.get(0)); | |
} | |
} | |
} else { | |
annotation.setFeatureValue(feature, null); | |
} | |
} else if (value instanceof ITypeExpression && !range.isPrimitive()) { | |
ITypeExpression typeExpr = (ITypeExpression) value; | |
Type t = typeExpr.getType(context, this); | |
assignAnnotationByTypeInWindow(annotation, feature, context, t); | |
} else if (value instanceof GenericFeatureExpression && !range.isPrimitive()) { | |
FeatureExpression fe = ((GenericFeatureExpression) value).getFeatureExpression(); | |
Type t = fe.getInitialType(context, this); | |
List<AnnotationFS> inWindow = getAnnotationsInWindow(context.getAnnotation(), t); | |
if (fe instanceof SimpleFeatureExpression) { | |
SimpleFeatureExpression sfe = (SimpleFeatureExpression) fe; | |
List<? extends FeatureStructure> featureAnnotations = null; | |
if (fe.getFeatures(context, this) != null) { | |
featureAnnotations = new ArrayList<>( | |
sfe.getFeatureStructures(inWindow, false, context, this)); | |
} else { | |
featureAnnotations = inWindow; | |
} | |
if (range.isArray()) { | |
annotation.setFeatureValue(feature, UIMAUtils.toFSArray(getJCas(), featureAnnotations)); | |
} else if (!featureAnnotations.isEmpty()) { | |
FeatureStructure a = featureAnnotations.get(0); | |
annotation.setFeatureValue(feature, a); | |
} | |
} else { | |
if (range.isArray()) { | |
annotation.setFeatureValue(feature, UIMAUtils.toFSArray(getJCas(), inWindow)); | |
} else { | |
AnnotationFS a = inWindow.get(0); | |
annotation.setFeatureValue(feature, a); | |
} | |
} | |
} | |
} | |
private void assignAnnotationByTypeInWindow(FeatureStructure annotation, Feature feature, | |
MatchContext context, Type type) { | |
List<AnnotationFS> inWindow = getAnnotationsInWindow(context.getAnnotation(), type); | |
if (feature.getRange().isArray()) { | |
annotation.setFeatureValue(feature, UIMAUtils.toFSArray(getJCas(), inWindow)); | |
} else { | |
if (inWindow != null && !inWindow.isEmpty()) { | |
AnnotationFS a = inWindow.get(0); | |
annotation.setFeatureValue(feature, a); | |
} else { | |
annotation.setFeatureValue(feature, null); | |
} | |
} | |
} | |
public void assignVariable(String var, IRutaExpression expression, MatchContext context) { | |
RutaBlock parent = context.getParent(); | |
RutaEnvironment environment = parent.getEnvironment(); | |
Class<?> clazz = environment.getVariableType(var); | |
if (clazz.equals(Double.class) && expression instanceof INumberExpression) { | |
double v = ((INumberExpression) expression).getDoubleValue(context, this); | |
environment.setVariableValue(var, v); | |
} else if (clazz.equals(Float.class) && expression instanceof INumberExpression) { | |
float v = (float) ((INumberExpression) expression).getDoubleValue(context, this); | |
environment.setVariableValue(var, v); | |
} else if (clazz.equals(Integer.class) && expression instanceof INumberExpression) { | |
int v = ((INumberExpression) expression).getIntegerValue(context, this); | |
environment.setVariableValue(var, v); | |
} else if (clazz.equals(Type.class) && expression instanceof ITypeExpression) { | |
Type v = ((ITypeExpression) expression).getType(context, this); | |
environment.setVariableValue(var, v); | |
} else if (clazz.equals(Boolean.class) && expression instanceof IBooleanExpression) { | |
boolean v = ((IBooleanExpression) expression).getBooleanValue(context, this); | |
environment.setVariableValue(var, v); | |
} else if (clazz.equals(String.class) && expression instanceof IStringExpression) { | |
String v = ((IStringExpression) expression).getStringValue(context, this); | |
environment.setVariableValue(var, v); | |
} else if (clazz.equals(AnnotationFS.class) && expression instanceof IAnnotationExpression) { | |
AnnotationFS v = ((IAnnotationExpression) expression).getAnnotation(context, this); | |
environment.setVariableValue(var, v); | |
} else if (clazz.equals(List.class)) { | |
Class<?> variableGenericType = environment.getVariableGenericType(var); | |
if (variableGenericType.equals(AnnotationFS.class) | |
&& expression instanceof IAnnotationListExpression) { | |
List<AnnotationFS> v = ((IAnnotationListExpression) expression).getAnnotationList(context, | |
this); | |
environment.setVariableValue(var, v); | |
} else if (variableGenericType.equals(Boolean.class) | |
&& expression instanceof IBooleanListExpression) { | |
List<Boolean> v = ((IBooleanListExpression) expression).getBooleanList(context, this); | |
environment.setVariableValue(var, v); | |
} else if (Number.class.isAssignableFrom(variableGenericType) | |
&& expression instanceof INumberListExpression) { | |
List<Number> v = ((INumberListExpression) expression).getNumberList(context, this); | |
environment.setVariableValue(var, v); | |
} else if (variableGenericType.equals(String.class) | |
&& expression instanceof IStringListExpression) { | |
List<String> v = ((IStringListExpression) expression).getStringList(context, this); | |
environment.setVariableValue(var, v); | |
} else if (variableGenericType.equals(Type.class) | |
&& expression instanceof ITypeListExpression) { | |
List<Type> v = ((ITypeListExpression) expression).getTypeList(context, this); | |
environment.setVariableValue(var, v); | |
} | |
} else if (clazz.equals(Boolean.class) && expression instanceof AnnotationTypeExpression) { | |
// special not yet supported use case: b = a1==a2 | |
// TODO: this should be solved by having a boolean expression and an atomic feature | |
// expression? | |
AnnotationTypeExpression ate = (AnnotationTypeExpression) expression; | |
AnnotationFS annotation = ate.getAnnotation(context, this); | |
FeatureExpression featureExpression = ate.getFeatureExpression(); | |
if (featureExpression instanceof FeatureMatchExpression) { | |
FeatureMatchExpression fme = (FeatureMatchExpression) featureExpression; | |
IRutaExpression arg = fme.getArg(); | |
if (arg instanceof IAnnotationExpression) { | |
AnnotationFS argAnnotation = ((IAnnotationExpression) arg).getAnnotation(context, this); | |
if (StringUtils.equals(fme.getOp(), "==")) { | |
environment.setVariableValue(var, annotation == argAnnotation); | |
} else if (StringUtils.equals(fme.getOp(), "!=")) { | |
environment.setVariableValue(var, annotation != argAnnotation); | |
} | |
} | |
} | |
} | |
} | |
@SuppressWarnings("unchecked") | |
public <T extends Object> T getExpressionValue(Class<T> clazz, IRutaExpression expression, | |
MatchContext context) { | |
if (clazz.equals(Double.class) && expression instanceof INumberExpression) { | |
double v = ((INumberExpression) expression).getDoubleValue(context, this); | |
return (T) Double.valueOf(v); | |
} else if (clazz.equals(Float.class) && expression instanceof INumberExpression) { | |
float v = (float) ((INumberExpression) expression).getDoubleValue(context, this); | |
return (T) Float.valueOf(v); | |
} else if (clazz.equals(Integer.class) && expression instanceof INumberExpression) { | |
int v = ((INumberExpression) expression).getIntegerValue(context, this); | |
return (T) Integer.valueOf(v); | |
} else if (clazz.equals(Type.class) && expression instanceof ITypeExpression) { | |
Type v = ((ITypeExpression) expression).getType(context, this); | |
return (T) v; | |
} else if (clazz.equals(Boolean.class) && expression instanceof IBooleanExpression) { | |
boolean v = ((IBooleanExpression) expression).getBooleanValue(context, this); | |
return (T) Boolean.valueOf(v); | |
} else if (clazz.equals(String.class) && expression instanceof IStringExpression) { | |
String v = ((IStringExpression) expression).getStringValue(context, this); | |
return (T) v; | |
} else if (clazz.equals(AnnotationFS.class) && expression instanceof IAnnotationExpression) { | |
AnnotationFS v = ((IAnnotationExpression) expression).getAnnotation(context, this); | |
return (T) v; | |
} | |
return null; | |
} | |
public AnnotationFS getSingleAnnotationByTypeInContext(Type type, MatchContext context) { | |
List<AnnotationFS> inWindow = getAnnotationsInWindow(context.getAnnotation(), type); | |
if (inWindow != null && !inWindow.isEmpty()) { | |
return inWindow.get(0); | |
} | |
return null; | |
} | |
public List<AnnotationFS> getAnnotationsByTypeInContext(Type type, MatchContext context) { | |
List<AnnotationFS> inWindow = getAnnotationsInWindow(context.getAnnotation(), type); | |
return inWindow; | |
} | |
public List<AnnotationFS> getBestGuessedAnnotationsAt(AnnotationFS window, Type type) { | |
List<AnnotationFS> result = new ArrayList<>(); | |
if (window == null || type == null) { | |
return result; | |
} | |
TypeSystem typeSystem = getCas().getTypeSystem(); | |
if (typeSystem.subsumes(type, window.getType())) { | |
result.add(window); | |
} else { | |
Collection<AnnotationFS> beginAnchors = getBeginAnchor(window.getBegin()) | |
.getBeginAnchors(type); | |
Collection<AnnotationFS> endAnchors = getEndAnchor(window.getEnd()).getEndAnchors(type); | |
@SuppressWarnings("unchecked") | |
Collection<AnnotationFS> intersection = CollectionUtils.intersection(beginAnchors, | |
endAnchors); | |
result.addAll(intersection); | |
} | |
return result; | |
} | |
public void changeOffsets(AnnotationFS annotation, int begin, int end, | |
AbstractRuleMatch<? extends AbstractRule> modifikator) { | |
if (!(annotation instanceof Annotation)) { | |
return; | |
} | |
Annotation a = (Annotation) annotation; | |
if (annotation.getBegin() == begin && annotation.getEnd() == end) { | |
return; | |
} | |
// TODO implement incremental reindexing | |
removeAnnotation(a); | |
a.setBegin(begin); | |
a.setEnd(end); | |
addAnnotation(a, true, modifikator); | |
} | |
public void changeBegin(AnnotationFS annotation, int begin, | |
AbstractRuleMatch<? extends AbstractRule> modifikator) { | |
changeOffsets(annotation, begin, annotation.getEnd(), modifikator); | |
} | |
public void changeEnd(AnnotationFS annotation, int end, | |
AbstractRuleMatch<? extends AbstractRule> modifikator) { | |
changeOffsets(annotation, annotation.getBegin(), end, modifikator); | |
} | |
public AnnotationFS getVeryFirstBeforeWindow(boolean direction) { | |
if (direction) { | |
RutaBasic firstBasicOfAll = getFirstBasicOfAll(); | |
int begin = firstBasicOfAll.getBegin(); | |
if (begin == 0) { | |
return documentBeginAnchor; | |
} else { | |
return getEndAnchor(begin); | |
} | |
} else { | |
RutaBasic lastBasicOfAll = getLastBasicOfAll(); | |
int end = lastBasicOfAll.getEnd(); | |
if (end == cas.getDocumentAnnotation().getEnd()) { | |
return documentEndAnchor; | |
} else { | |
return getBeginAnchor(end); | |
} | |
} | |
} | |
public Type getSharedParentType(List<Type> types) { | |
if (types == null || types.isEmpty()) { | |
return cas.getAnnotationType(); | |
} | |
if (types.size() == 1) { | |
return types.get(0); | |
} | |
TypeSystem typeSystem = cas.getTypeSystem(); | |
Type parentType = types.get(0); | |
for (Type type : types) { | |
parentType = getSharedParentType(parentType, type, typeSystem); | |
} | |
return parentType; | |
} | |
private Type getSharedParentType(Type type1, Type type2, TypeSystem typeSystem) { | |
if (cas.getAnnotationType().equals(type1) || cas.getAnnotationType().equals(type2)) { | |
return cas.getAnnotationType(); | |
} | |
if (type1.equals(type2)) { | |
return type1; | |
} | |
if (typeSystem.subsumes(type1, type2)) { | |
return type1; | |
} | |
if (typeSystem.subsumes(type2, type1)) { | |
return type2; | |
} | |
Type parentType = typeSystem.getParent(type1); | |
while (parentType != null && !cas.getAnnotationType().equals(parentType)) { | |
if (typeSystem.subsumes(parentType, type2)) { | |
return parentType; | |
} | |
} | |
return cas.getAnnotationType(); | |
} | |
public RutaAnnotation getRutaAnnotationFor(AnnotationFS annotation, boolean create, | |
RutaStream stream) { | |
Type heuristicType = cas.getTypeSystem().getType(RutaAnnotation.class.getName()); | |
List<AnnotationFS> ras = CasUtil.selectAt(cas, heuristicType, annotation.getBegin(), | |
annotation.getEnd()); | |
for (AnnotationFS each : ras) { | |
if (((RutaAnnotation) each).getAnnotation() == annotation) { | |
return (RutaAnnotation) each; | |
} | |
} | |
if (create) { | |
JCas jCas = stream.getJCas(); | |
RutaAnnotation result = new RutaAnnotation(jCas, annotation.getBegin(), annotation.getEnd()); | |
result.setAnnotation((Annotation) annotation); | |
result.addToIndexes(); | |
return result; | |
} | |
return null; | |
} | |
private Collection<Type> removeSubsumedTypes(Collection<Type> types, TypeSystem typeSystem) { | |
List<Type> rootTypes = new ArrayList<>(types); | |
for (Type type1 : types) { | |
for (Type type2 : types) { | |
if (type1 != type2 && typeSystem.subsumes(type1, type2)) { | |
rootTypes.remove(type2); | |
} | |
} | |
} | |
return rootTypes; | |
} | |
private Collection<Type> expandToAllSubtypes(Collection<Type> reindexTypeList, | |
Collection<Type> reindexSkipTypes, TypeSystem typeSystem) { | |
if (reindexTypeList.isEmpty()) { | |
return Collections.emptyList(); | |
} | |
Collection<Type> result = new LinkedHashSet<>(); | |
for (Type type : reindexTypeList) { | |
if (skipTypeForIndexing(type, reindexSkipTypes, typeSystem)) { | |
continue; | |
} | |
result.add(type); | |
List<Type> properlySubsumedTypes = typeSystem.getProperlySubsumedTypes(type); | |
for (Type subType : properlySubsumedTypes) { | |
if (skipTypeForIndexing(subType, reindexSkipTypes, typeSystem)) { | |
continue; | |
} | |
result.add(subType); | |
} | |
// if we started with uima.tcas.Annotation, we already collected all | |
if (type.getName().equals(CAS.TYPE_NAME_ANNOTATION)) { | |
return result; | |
} | |
} | |
return result; | |
} | |
private Collection<Type> convertNamesToTypes(String[] typeNames, TypeSystem typeSystem) { | |
if (typeNames == null) { | |
return Collections.emptyList(); | |
} | |
Collection<Type> result = new ArrayList<>(typeNames.length); | |
for (String each : typeNames) { | |
Type type = typeSystem.getType(each); | |
if (type != null) { | |
result.add(type); | |
} | |
} | |
return result; | |
} | |
public void setMaxRuleMatches(long maxRuleMatches) { | |
this.maxRuleMatches = maxRuleMatches; | |
} | |
public void setMaxRuleElementMatches(long maxRuleElementMatches) { | |
this.maxRuleElementMatches = maxRuleElementMatches; | |
} | |
public long getMaxRuleMatches() { | |
return maxRuleMatches; | |
} | |
public long getMaxRuleElementMatches() { | |
return maxRuleElementMatches; | |
} | |
} |