blob: 6b99ab1f18d3408e57b9966d17479a1cbbc9b5c3 [file] [log] [blame]
/*
* 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 static java.util.Arrays.asList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.apache.uima.cas.text.AnnotationFS;
import org.apache.uima.ruta.RutaElement;
import org.apache.uima.ruta.RutaStatement;
import org.apache.uima.ruta.RutaStream;
import org.apache.uima.ruta.ScriptApply;
import org.apache.uima.ruta.action.AbstractRutaAction;
import org.apache.uima.ruta.block.RutaBlock;
import org.apache.uima.ruta.condition.AbstractRutaCondition;
import org.apache.uima.ruta.rule.quantifier.NormalQuantifier;
import org.apache.uima.ruta.rule.quantifier.RuleElementQuantifier;
import org.apache.uima.ruta.visitor.InferenceCrowd;
public abstract class AbstractRuleElement extends RutaElement implements RuleElement {
protected static final InferenceCrowd EMPTY_CROWD = new InferenceCrowd(Collections.emptyList());
protected RuleElementQuantifier quantifier;
protected List<AbstractRutaCondition> conditions;
protected List<AbstractRutaAction> actions;
private boolean startAnchor;
private String label;
private RuleElementContainer container;
protected RutaBlock parent;
protected List<List<RutaStatement>> inlinedConditionRuleBlocks = new ArrayList<>();
protected List<List<RutaStatement>> inlinedActionRuleBlocks = new ArrayList<>();
public AbstractRuleElement(RuleElementQuantifier quantifier,
List<AbstractRutaCondition> conditions, List<AbstractRutaAction> actions,
RuleElementContainer container, RutaBlock parent) {
super();
this.quantifier = quantifier;
this.conditions = conditions;
this.actions = actions;
this.container = container;
this.parent = parent;
if (this.conditions == null) {
this.conditions = new ArrayList<>();
}
if (this.actions == null) {
this.actions = new ArrayList<>();
}
if (this.quantifier == null) {
this.quantifier = new NormalQuantifier();
}
}
@Override
public List<RuleMatch> continueSideStep(boolean after, RuleMatch ruleMatch, RuleApply ruleApply,
ComposedRuleElementMatch containerMatch, RuleElement entryPoint, RutaStream stream,
InferenceCrowd crowd) {
List<RuleMatch> result = new ArrayList<>();
boolean newDirection = !after;
List<AnnotationFS> matchedAnnotationsOf = ruleMatch.getMatchedAnnotationsOfElement(this);
AnnotationFS annotation = null;
if (!matchedAnnotationsOf.isEmpty()) {
if (newDirection) {
annotation = matchedAnnotationsOf.get(matchedAnnotationsOf.size() - 1);
} else {
annotation = matchedAnnotationsOf.get(0);
}
ComposedRuleElementMatch sideStepContainerMatch = containerMatch;
if (!containerMatch.getRuleElement().equals(getContainer())) {
List<List<RuleElementMatch>> matchInfo = ruleMatch
.getMatchInfo((ComposedRuleElement) getContainer());
if (newDirection) {
List<RuleElementMatch> list = matchInfo.get(matchInfo.size() - 1);
sideStepContainerMatch = (ComposedRuleElementMatch) list.get(list.size() - 1);
} else {
List<RuleElementMatch> list = matchInfo.get(0);
sideStepContainerMatch = (ComposedRuleElementMatch) list.get(0);
}
}
MatchContext context = new MatchContext(this, ruleMatch, newDirection);
if (quantifier.continueMatch(newDirection, context, annotation, sideStepContainerMatch,
stream, crowd)) {
continueMatch(newDirection, annotation, ruleMatch, ruleApply, sideStepContainerMatch, null,
entryPoint, stream, crowd);
} else {
RuleElement nextRuleElement = getContainer().getNextElement(newDirection, this);
if (nextRuleElement != null) {
result = nextRuleElement.continueMatch(newDirection, annotation, ruleMatch, ruleApply,
sideStepContainerMatch, null, null, stream, crowd);
} else if (getContainer() instanceof ComposedRuleElement) {
ComposedRuleElement composed = (ComposedRuleElement) getContainer();
result = composed.fallbackContinue(newDirection, false, annotation, ruleMatch, ruleApply,
sideStepContainerMatch, null, entryPoint, stream, crowd);
}
}
}
return result;
}
protected void doneMatching(RuleMatch ruleMatch, RuleApply ruleApply, RuleElement entryPoint,
RutaStream stream, InferenceCrowd crowd) {
if (!ruleMatch.isApplied()) {
ruleApply.add(ruleMatch, stream);
RutaRule rule = ruleMatch.getRule();
Collection<String> localVariables = rule.getLabels();
if (ruleMatch.matchedCompletely()) {
rule.getEnvironment().acceptTempVariableValues(localVariables);
rule.getRoot().applyRuleElements(ruleMatch, stream, crowd);
} else if (entryPoint == null) {
// there was no dynamic lookahead, remove temp vars
rule.getEnvironment().clearTempVariables(localVariables);
}
ruleMatch.setApplied(true);
}
}
protected void processInlinedActionRules(RuleMatch ruleMatch, RutaStream stream,
InferenceCrowd crowd) {
List<List<RuleElementMatch>> matchInfo = ruleMatch.getMatchInfo(this);
// TODO: which rule element match should be used? all? should context matter?
if (matchInfo == null || matchInfo.isEmpty() || matchInfo.get(0) == null
|| matchInfo.get(0).isEmpty()) {
return;
}
RuleElementMatch ruleElementMatch = matchInfo.get(0).get(0);
processInlinedActionRules(ruleMatch, ruleElementMatch, stream, crowd);
}
protected List<List<ScriptApply>> processInlinedActionRules(RuleMatch ruleMatch,
RuleElementMatch ruleElementMatch, RutaStream stream, InferenceCrowd crowd) {
if (inlinedActionRuleBlocks != null && !inlinedActionRuleBlocks.isEmpty()) {
List<List<ScriptApply>> inlinedBlocksApplies = processInlinedRules(inlinedActionRuleBlocks,
ruleMatch, stream, crowd);
ruleElementMatch.setInlinedActionRules(inlinedBlocksApplies);
return inlinedBlocksApplies;
}
return null;
}
protected List<List<ScriptApply>> processInlinedConditionRules(RuleMatch ruleMatch,
RuleElementMatch ruleElementMatch, RutaStream stream, InferenceCrowd crowd) {
if (inlinedConditionRuleBlocks != null && !inlinedConditionRuleBlocks.isEmpty()) {
List<List<ScriptApply>> inlinedBlocksApplies = processInlinedRules(inlinedConditionRuleBlocks,
ruleMatch, stream, crowd);
ruleElementMatch.setInlinedConditionRules(inlinedBlocksApplies);
return inlinedBlocksApplies;
}
return null;
}
protected List<List<ScriptApply>> processInlinedRules(List<List<RutaStatement>> inlinedRuleBlocks,
RuleMatch ruleMatch, RutaStream stream, InferenceCrowd crowd) {
List<List<ScriptApply>> result = new ArrayList<>();
List<AnnotationFS> matchedAnnotationsOf = ruleMatch.getMatchedAnnotationsOfElement(this);
for (AnnotationFS annotationFS : matchedAnnotationsOf) {
RutaStream windowStream = stream.getWindowStream(annotationFS, annotationFS.getType());
for (List<RutaStatement> inlinedRules : inlinedRuleBlocks) {
List<ScriptApply> blockResult = new ArrayList<>();
for (RutaStatement each : inlinedRules) {
ScriptApply apply = each.apply(windowStream, crowd);
blockResult.add(apply);
}
result.add(blockResult);
}
}
return result;
}
@Override
public void apply(RuleMatch ruleMatch, RutaStream stream, InferenceCrowd crowd) {
for (AbstractRutaAction action : actions) {
crowd.beginVisit(action, null);
action.execute(new MatchContext(this, ruleMatch), stream, crowd);
crowd.endVisit(action, null);
}
processInlinedActionRules(ruleMatch, stream, crowd);
}
protected boolean matchInlinedRules(RuleMatch ruleMatch, RuleElementMatch ruleElementMatch,
RutaStream stream, InferenceCrowd crowd) {
List<List<ScriptApply>> blockResults = processInlinedConditionRules(ruleMatch, ruleElementMatch,
stream, crowd);
if (blockResults == null) {
return true;
}
boolean matched = true;
for (List<ScriptApply> list : blockResults) {
matched &= atLeastOneRuleMatched(list);
}
return matched;
}
private boolean atLeastOneRuleMatched(List<ScriptApply> list) {
for (ScriptApply scriptApply : list) {
if (scriptApply instanceof RuleApply) {
RuleApply ra = (RuleApply) scriptApply;
if (ra.applied > 0) {
return true;
}
}
}
return false;
}
protected List<RuleElementMatch> getMatch(RuleMatch ruleMatch,
ComposedRuleElementMatch containerMatch) {
List<RuleElementMatch> matchInfo;
if (containerMatch != null) {
matchInfo = containerMatch.getInnerMatches().get(this);
} else {
matchInfo = ruleMatch.getMatchInfo(this).get(0);
}
return matchInfo;
}
@Override
public List<RuleElementMatch> evaluateMatches(List<RuleElementMatch> matches,
MatchContext context, RutaStream stream) {
return quantifier.evaluateMatches(matches, context, stream, EMPTY_CROWD);
}
@Override
public List<Integer> getSelfIndexList() {
if (getContainer() == null) {
return null;
}
return asList(getContainer().getRuleElements().indexOf(this) + 1);
}
@Override
public boolean hasAncestor(boolean after) {
RuleElementContainer c = getContainer();
if (c == null) {
return false;
}
RuleElement nextElement = c.getNextElement(after, this);
if (nextElement != null) {
return true;
}
if (c instanceof ComposedRuleElement) {
return ((ComposedRuleElement) c).hasAncestor(after);
}
return false;
}
protected boolean earlyExit(AnnotationFS eachAnchor, RuleApply ruleApply, RutaStream stream) {
if (stream.isGreedyAnchoring() && ruleApply != null
&& isAlreadyCovered(eachAnchor, ruleApply, stream)) {
// skip if next matched should not overlap
return true;
}
if (stream.isOnlyOnce() && ruleApply != null && ruleApply.getApplied() > 0) {
// skip if the rule should only be applied once, on the first successful match
return true;
}
return false;
}
private boolean isAlreadyCovered(AnnotationFS eachAnchor, RuleApply ruleApply,
RutaStream stream) {
if (eachAnchor == null) {
return false;
}
List<AbstractRuleMatch<? extends AbstractRule>> list = ruleApply.getList();
Collections.reverse(list);
for (AbstractRuleMatch<? extends AbstractRule> each : list) {
if (each instanceof RuleMatch) {
RuleMatch rm = (RuleMatch) each;
List<AnnotationFS> matchedAnnotationsOf = Collections.emptyList();
if (stream.isGreedyRule()) {
matchedAnnotationsOf = rm.getMatchedAnnotationsOfRoot();
} else if (stream.isGreedyRuleElement()) {
matchedAnnotationsOf = rm.getMatchedAnnotationsOfElement(this);
}
for (AnnotationFS annotationFS : matchedAnnotationsOf) {
if (eachAnchor.getBegin() >= annotationFS.getBegin()
&& eachAnchor.getEnd() <= annotationFS.getEnd()) {
return true;
} else if (eachAnchor.getBegin() < annotationFS.getEnd()) {
// overlapping annotations of composed rule elements with null ruleApply lookahead
return true;
}
}
}
}
return false;
}
@Override
public RuleElementQuantifier getQuantifier() {
return quantifier;
}
@Override
public RutaBlock getParent() {
return parent;
}
@Override
public List<AbstractRutaCondition> getConditions() {
return conditions;
}
public void setConditions(List<AbstractRutaCondition> conditions) {
this.conditions = conditions;
}
@Override
public List<AbstractRutaAction> getActions() {
return actions;
}
public void setActions(List<AbstractRutaAction> actions) {
this.actions = actions;
}
public void setQuantifier(RuleElementQuantifier quantifier) {
this.quantifier = quantifier;
}
@Override
public RuleElementContainer getContainer() {
return container;
}
@Override
public void setContainer(RuleElementContainer container) {
this.container = container;
}
@Override
public void setStartAnchor(boolean start) {
startAnchor = start;
}
@Override
public boolean isStartAnchor() {
return startAnchor;
}
@Override
public String getLabel() {
return label;
}
@Override
public void setLabel(String label) {
this.label = label;
}
@Override
public void addInlinedConditionRules(List<RutaStatement> innerRules) {
inlinedConditionRuleBlocks.add(innerRules);
}
@Override
public List<List<RutaStatement>> getInlinedConditionRuleBlocks() {
return inlinedConditionRuleBlocks;
}
@Override
public void addInlinedActionRules(List<RutaStatement> innerRules) {
inlinedActionRuleBlocks.add(innerRules);
}
@Override
public List<List<RutaStatement>> getInlinedActionRuleBlocks() {
return inlinedActionRuleBlocks;
}
}