/* | |
* 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.rule; | |
import java.util.HashMap; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Map.Entry; | |
import org.apache.commons.lang3.StringUtils; | |
import org.apache.uima.cas.text.AnnotationFS; | |
import org.apache.uima.ruta.RutaConstants; | |
import org.apache.uima.ruta.RutaEnvironment; | |
import org.apache.uima.ruta.RutaStatement; | |
import org.apache.uima.ruta.RutaStream; | |
import org.apache.uima.ruta.block.RutaBlock; | |
import org.apache.uima.ruta.visitor.InferenceCrowd; | |
public class RutaRule extends AbstractRule { | |
private ComposedRuleElement root; | |
/** | |
* labels of all rule elements including those in inlined rules. | |
* The values store the values of overridden variables. | |
*/ | |
private Map<String, Object> labels; | |
public RutaRule(List<RuleElement> elements, RutaBlock parent, int id) { | |
super(parent, id); | |
this.root = new ComposedRuleElement(elements, null, null, null, null, parent); | |
this.labels = new HashMap<>(); | |
} | |
@Override | |
public RuleApply apply(RutaStream stream, InferenceCrowd crowd) { | |
return apply(stream, crowd, stream.isGreedyAnchoring()); | |
} | |
public RuleApply apply(RutaStream stream, InferenceCrowd crowd, boolean remember) { | |
RuleApply ruleApply = new RuleApply(this, remember); | |
MatchContext context = new MatchContext(getParent()); | |
prepareEnvironment(context, stream); | |
crowd.beginVisit(this, ruleApply); | |
RuleMatch ruleMatch = new RuleMatch(this); | |
root.startMatch(ruleMatch, ruleApply, null, null, stream, crowd); | |
crowd.endVisit(this, ruleApply); | |
cleanupEnvironment(context, stream); | |
return ruleApply; | |
} | |
@Override | |
public String toString() { | |
return root == null ? "<empty>" : root.toString(); | |
} | |
public final List<RuleElement> getRuleElements() { | |
return root.getRuleElements(); | |
} | |
@Override | |
public RutaEnvironment getEnvironment() { | |
return getParent().getEnvironment(); | |
} | |
public void setRuleElements(List<RuleElement> elements) { | |
if (elements != null && elements.size() == 1 | |
&& elements.get(0) instanceof ConjunctRulesRuleElement) { | |
root = (ComposedRuleElement) elements.get(0); | |
} else { | |
root.setRuleElements(elements); | |
} | |
if (elements != null) { | |
// update label map | |
for (RuleElement ruleElement : elements) { | |
fillLabelMap(ruleElement); | |
} | |
} | |
} | |
private void fillLabelMap(RuleElement ruleElement) { | |
if (!StringUtils.isBlank(ruleElement.getLabel())) { | |
labels.put(ruleElement.getLabel(), null); | |
} | |
if (ruleElement instanceof ComposedRuleElement) { | |
ComposedRuleElement cre = (ComposedRuleElement) ruleElement; | |
List<RuleElement> ruleElements = cre.getRuleElements(); | |
for (RuleElement each : ruleElements) { | |
fillLabelMap(each); | |
} | |
} | |
fillLabelMapWithInlinedRules(ruleElement.getInlinedConditionRules()); | |
fillLabelMapWithInlinedRules(ruleElement.getInlinedActionRules()); | |
} | |
private void fillLabelMapWithInlinedRules(List<RutaStatement> rules) { | |
if (rules != null) { | |
for (RutaStatement eachInlined : rules) { | |
if (eachInlined instanceof RutaRule) { | |
RutaRule inlinedRule = (RutaRule) eachInlined; | |
inlinedRule.setInlined(true); | |
fillLabelMap(inlinedRule.getRoot()); | |
} | |
} | |
} | |
} | |
private void prepareEnvironment(MatchContext context, RutaStream stream) { | |
if(isInlined()) { | |
// only the actual rule may setup the environment | |
return; | |
} | |
RutaBlock parent = context.getParent(); | |
RutaEnvironment environment = parent.getEnvironment(); | |
for (Entry<String, Object> entry : labels.entrySet()) { | |
String label = entry.getKey(); | |
if(environment.isVariable(label)) { | |
Class<?> variableType = environment.getVariableType(label); | |
Class<?> variableGenericType = environment.getVariableGenericType(label); | |
if(variableType != null && variableGenericType!= null && variableType.isAssignableFrom(List.class) && variableGenericType.isAssignableFrom(AnnotationFS.class)) { | |
labels.put(label, environment.getVariableValue(label, stream)); | |
} else if(variableType != null && variableType.isAssignableFrom(AnnotationFS.class)) { | |
} else { | |
String type = variableType== null ? "unknown" : variableType.getSimpleName(); | |
throw new RuntimeException("Overriding global variable '"+label+"' of type '"+ type+ "' with a local label variable is not allowed!"); | |
} | |
} else { | |
environment.addVariable(label, RutaConstants.RUTA_VARIABLE_ANNOTATION_LIST); | |
} | |
} | |
} | |
private void cleanupEnvironment(MatchContext context, RutaStream stream) { | |
if(isInlined()) { | |
// only the actual rule may revert the environment | |
return; | |
} | |
RutaBlock parent = context.getParent(); | |
RutaEnvironment environment = parent.getEnvironment(); | |
for (Entry<String, Object> entry : labels.entrySet()) { | |
String label = entry.getKey(); | |
Object value = entry.getValue(); | |
if(value == null) { | |
environment.removeVariable(label); | |
} else { | |
environment.setVariableValue(label, value); | |
} | |
} | |
} | |
public ComposedRuleElement getRoot() { | |
return root; | |
} | |
} |