/* | |
* 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.expression.feature; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.List; | |
import org.apache.commons.lang3.StringUtils; | |
import org.apache.uima.cas.Feature; | |
import org.apache.uima.cas.FeatureStructure; | |
import org.apache.uima.cas.Type; | |
import org.apache.uima.cas.text.AnnotationFS; | |
import org.apache.uima.jcas.cas.FSArray; | |
import org.apache.uima.ruta.RutaStream; | |
import org.apache.uima.ruta.UIMAConstants; | |
import org.apache.uima.ruta.expression.MatchReference; | |
import org.apache.uima.ruta.expression.annotation.IAnnotationExpression; | |
import org.apache.uima.ruta.expression.type.ITypeExpression; | |
import org.apache.uima.ruta.rule.MatchContext; | |
import org.apache.uima.ruta.utils.IndexedReference; | |
import org.apache.uima.ruta.utils.ParsingUtils; | |
public class SimpleFeatureExpression extends FeatureExpression { | |
private MatchReference mr; | |
public SimpleFeatureExpression(MatchReference mr) { | |
super(); | |
this.mr = mr; | |
} | |
@Override | |
public Feature getFeature(MatchContext context, RutaStream stream) { | |
List<Feature> features = this.getFeatures(context, stream); | |
if (features != null && !features.isEmpty()) { | |
Feature feature = features.get(features.size() - 1); | |
if (feature instanceof LazyFeature) { | |
LazyFeature lazyFeature = (LazyFeature) feature; | |
AnnotationFS annotation = context.getAnnotation(); | |
List<AnnotationFS> targetAnnotation = this.getTargetAnnotation(annotation, this, context, | |
stream); | |
if (targetAnnotation != null && !targetAnnotation.isEmpty()) { | |
annotation = targetAnnotation.get(0); | |
} | |
if (features.size() == 1) { | |
feature = lazyFeature.initialize(annotation); | |
} | |
} | |
return feature; | |
} else { | |
return null; | |
} | |
} | |
@Override | |
public List<Feature> getFeatures(MatchContext context, RutaStream stream) { | |
List<Feature> result = new ArrayList<Feature>(); | |
Type type = this.getInitialType(context, stream); | |
Feature feature = null; | |
for (String each : this.getFeatureStringList(context, stream)) { | |
IndexedReference indexedReference = ParsingUtils.parseIndexedReference(each); | |
if (indexedReference.index != -1) { | |
Feature delegate = type.getFeatureByBaseName(indexedReference.reference); | |
if (delegate != null) { | |
feature = new IndexedFeature(delegate, indexedReference.index); | |
} else { | |
throw new IllegalArgumentException("Not able to access feature " + each + " of type " | |
+ type.getName() + "in script " + context.getParent().getName()); | |
} | |
} else if (StringUtils.equals(each, UIMAConstants.FEATURE_COVERED_TEXT) | |
|| StringUtils.equals(each, UIMAConstants.FEATURE_COVERED_TEXT_SHORT)) { | |
if (type != null) { | |
feature = type.getFeatureByBaseName(each); | |
if (feature == null) { | |
// there is no explicit feature for coveredText | |
feature = new CoveredTextFeature(); | |
} | |
} else { | |
// also allow for unknown types | |
feature = new CoveredTextFeature(); | |
} | |
} else if (StringUtils.equals(each, UIMAConstants.FEATURE_TYPE)) { | |
feature = new TypeFeature(); | |
} else if (type == null || type.isArray()) { | |
// lazy check of range | |
feature = new LazyFeature(each, context.getParent()); | |
} else { | |
feature = type.getFeatureByBaseName(each); | |
if (feature == null) { | |
// type maybe not specific enough | |
feature = new LazyFeature(each, context.getParent()); | |
} | |
} | |
result.add(feature); | |
if (feature instanceof LazyFeature) { | |
type = null; | |
} else if (feature != null) { | |
type = feature.getRange(); | |
} | |
} | |
return result; | |
} | |
@Override | |
public Type getInitialType(MatchContext context, RutaStream stream) { | |
ITypeExpression typeExpression = this.mr.getTypeExpression(context, stream); | |
IAnnotationExpression annotationExpression = this.mr.getAnnotationExpression(context, stream); | |
IAnnotationExpression annotationListExpression = this.mr.getAnnotationExpression(context, | |
stream); | |
if (typeExpression != null) { | |
return typeExpression.getType(context, stream); | |
} else if (annotationExpression != null) { | |
AnnotationFS annotation = annotationExpression.getAnnotation(context, stream); | |
if (annotation != null) { | |
return annotation.getType(); | |
} | |
} else if (annotationListExpression != null) { | |
AnnotationFS annotation = annotationListExpression.getAnnotation(context, stream); | |
if (annotation != null) { | |
return annotation.getType(); | |
} | |
} | |
return null; | |
} | |
@Override | |
public List<String> getFeatureStringList(MatchContext context, RutaStream stream) { | |
return this.mr.getFeatureList(); | |
} | |
@Override | |
public Collection<? extends AnnotationFS> getAnnotations( | |
Collection<? extends FeatureStructure> featureStructures, boolean checkOnFeatureValue, | |
MatchContext context, RutaStream stream) { | |
Collection<AnnotationFS> result = new ArrayList<>(); | |
List<Feature> features = this.getFeatures(context, stream); | |
if (features != null && !features.isEmpty()) { | |
this.collectFeatureStructures(featureStructures, features, checkOnFeatureValue, true, result, | |
stream, context); | |
return result; | |
} else { | |
return this.filterAnnotations(featureStructures); | |
} | |
} | |
@Override | |
public Collection<? extends FeatureStructure> getFeatureStructures( | |
Collection<? extends FeatureStructure> featureStructures, boolean checkOnFeatureValue, | |
MatchContext context, RutaStream stream) { | |
Collection<FeatureStructure> result = new ArrayList<>(); | |
List<Feature> features = this.getFeatures(context, stream); | |
if (features != null && !features.isEmpty()) { | |
this.collectFeatureStructures(featureStructures, features, checkOnFeatureValue, false, result, | |
stream, context); | |
return result; | |
} else { | |
return featureStructures; | |
} | |
} | |
private <T> void collectFeatureStructures( | |
Collection<? extends FeatureStructure> featureStructures, List<Feature> features, | |
boolean checkOnFeatureValue, boolean onlyAnnotations, Collection<T> result, | |
RutaStream stream, MatchContext context) { | |
for (FeatureStructure each : featureStructures) { | |
this.collectFeatureStructures(each, features, checkOnFeatureValue, onlyAnnotations, null, | |
result, stream, context); | |
} | |
} | |
@SuppressWarnings("unchecked") | |
private <T> void collectFeatureStructures(FeatureStructure featureStructure, | |
List<Feature> features, boolean checkOnFeatureValue, boolean collectOnlyAnnotations, | |
T lastValidFeatureStructure, Collection<T> result, RutaStream stream, | |
MatchContext context) { | |
if (featureStructure == null) { | |
return; | |
} | |
if (!collectOnlyAnnotations) { | |
if (!featureStructure.getType().isArray()) { | |
lastValidFeatureStructure = (T) featureStructure; | |
} | |
} else if (featureStructure instanceof AnnotationFS) { | |
lastValidFeatureStructure = (T) featureStructure; | |
} | |
Feature currentFeature = null; | |
List<Feature> tail = null; | |
if (features != null && !features.isEmpty()) { | |
currentFeature = features.get(0); | |
if (currentFeature instanceof LazyFeature) { | |
LazyFeature lazyFeature = (LazyFeature) currentFeature; | |
Feature delegate = lazyFeature.initialize(featureStructure); | |
if (delegate == null) { | |
throw new RuntimeException("Invalid feature! Feature '" + lazyFeature.getFeatureName() | |
+ "' is not defined for type '" + featureStructure.getType() + "' in script " | |
+ context.getParent().getName() + "."); | |
} else { | |
currentFeature = delegate; | |
} | |
} | |
tail = features.subList(1, features.size()); | |
} | |
if (currentFeature == null || currentFeature instanceof CoveredTextFeature | |
|| currentFeature instanceof TypeFeature || currentFeature.getRange().isPrimitive()) { | |
// feature == null -> this is not a real feature | |
if (this instanceof FeatureMatchExpression) { | |
FeatureMatchExpression fme = (FeatureMatchExpression) this; | |
if (checkOnFeatureValue) { | |
if (fme.checkFeatureValue(featureStructure, currentFeature, context, stream)) { | |
result.add(lastValidFeatureStructure); | |
} | |
} else { | |
result.add(lastValidFeatureStructure); | |
} | |
} else { | |
result.add(lastValidFeatureStructure); | |
} | |
} else { | |
this.collectFeatureStructures(featureStructure, currentFeature, tail, checkOnFeatureValue, | |
collectOnlyAnnotations, lastValidFeatureStructure, result, stream, context); | |
} | |
} | |
private <T> void collectFeatureStructures(FeatureStructure featureStructure, | |
Feature currentFeature, List<Feature> tail, boolean checkOnFeatureValue, | |
boolean collectOnlyAnnotations, T lastValidFeatureStructure, Collection<T> result, | |
RutaStream stream, MatchContext context) { | |
// stop early for match expressions | |
if (this instanceof FeatureMatchExpression && (tail == null || tail.isEmpty())) { | |
FeatureMatchExpression fme = (FeatureMatchExpression) this; | |
if (checkOnFeatureValue) { | |
if (fme.checkFeatureValue(featureStructure, currentFeature, context, stream)) { | |
result.add(lastValidFeatureStructure); | |
} | |
} else { | |
result.add(lastValidFeatureStructure); | |
} | |
return; | |
} | |
int index = -1; | |
if (currentFeature instanceof IndexedFeature) { | |
IndexedFeature indexedFeature = (IndexedFeature) currentFeature; | |
currentFeature = indexedFeature.getDelegate(); | |
index = indexedFeature.getIndex(); | |
} | |
FeatureStructure value = featureStructure.getFeatureValue(currentFeature); | |
if (value instanceof AnnotationFS) { | |
AnnotationFS next = (AnnotationFS) value; | |
this.collectFeatureStructures(next, tail, checkOnFeatureValue, collectOnlyAnnotations, | |
lastValidFeatureStructure, result, stream, context); | |
} else if (value instanceof FSArray && index >= 0) { | |
FSArray<?> array = (FSArray<?>) value; | |
if (index < array.size()) { | |
FeatureStructure fs = array.get(index); | |
if (fs instanceof AnnotationFS) { | |
AnnotationFS next = (AnnotationFS) fs; | |
this.collectFeatureStructures(next, tail, checkOnFeatureValue, collectOnlyAnnotations, | |
lastValidFeatureStructure, result, stream, context); | |
} | |
} | |
} else if (value instanceof FSArray) { | |
FSArray<?> array = (FSArray<?>) value; | |
for (int i = 0; i < array.size(); i++) { | |
FeatureStructure fs = array.get(i); | |
this.collectFeatureStructures(fs, tail, checkOnFeatureValue, collectOnlyAnnotations, | |
lastValidFeatureStructure, result, stream, context); | |
} | |
} else if (value != null && !value.getType().isPrimitive()) { | |
// feature structure feature values | |
this.collectFeatureStructures(value, tail, checkOnFeatureValue, collectOnlyAnnotations, | |
lastValidFeatureStructure, result, stream, context); | |
} else if (value != null) { | |
// primitive? -> return last annotation for further processing | |
result.add(lastValidFeatureStructure); | |
// throw new IllegalArgumentException(value.getType() | |
// + " is not supported in a feature match expression (" + mr.getMatch() + ")."); | |
} | |
} | |
public MatchReference getMatchReference() { | |
return this.mr; | |
} | |
@Override | |
public String toString() { | |
return this.mr.getMatch(); | |
} | |
private Collection<AnnotationFS> filterAnnotations( | |
Collection<? extends FeatureStructure> featureStructures) { | |
Collection<AnnotationFS> result = new ArrayList<>(featureStructures.size()); | |
for (FeatureStructure featureStructure : featureStructures) { | |
if (featureStructure instanceof AnnotationFS) { | |
result.add((AnnotationFS) featureStructure); | |
} | |
} | |
return result; | |
} | |
} |