Merge pull request #74 from apache/bugfix/UIMA-6414-Ruta-missing-match-for-optional-after-sidestep-out-of-composed
UIMA-6414: Ruta: missing match for optional after sidestep out of composed
diff --git a/ruta-core/src/main/java/org/apache/uima/ruta/rule/AbstractRuleElement.java b/ruta-core/src/main/java/org/apache/uima/ruta/rule/AbstractRuleElement.java
index 6e134e1..f367d61 100644
--- a/ruta-core/src/main/java/org/apache/uima/ruta/rule/AbstractRuleElement.java
+++ b/ruta-core/src/main/java/org/apache/uima/ruta/rule/AbstractRuleElement.java
@@ -113,12 +113,16 @@
} else {
RuleElement nextRuleElement = getContainer().getNextElement(newDirection, this);
if (nextRuleElement != null) {
+ RuleElement sideStepOrigin = null;
+ if (getContainer() instanceof RuleElement) {
+ sideStepOrigin = (RuleElement) getContainer();
+ }
result = nextRuleElement.continueMatch(newDirection, annotation, ruleMatch, ruleApply,
- sideStepContainerMatch, null, null, stream, crowd);
+ sideStepContainerMatch, sideStepOrigin, entryPoint, stream, crowd);
} else if (getContainer() instanceof ComposedRuleElement) {
ComposedRuleElement composed = (ComposedRuleElement) getContainer();
result = composed.fallbackContinue(newDirection, false, annotation, ruleMatch, ruleApply,
- sideStepContainerMatch, null, entryPoint, stream, crowd);
+ sideStepContainerMatch, composed, entryPoint, stream, crowd);
}
}
}
@@ -127,7 +131,9 @@
protected void doneMatching(RuleMatch ruleMatch, RuleApply ruleApply, RutaStream stream,
InferenceCrowd crowd) {
- if (!ruleMatch.isApplied()) {
+ // do not execute actions if they already have been or if this is just a lookahead
+ // (ruleApply==null)
+ if (!ruleMatch.isApplied() && ruleApply != null) {
ruleApply.add(ruleMatch, stream);
if (ruleMatch.matchedCompletely()) {
RutaRule rule = ruleMatch.getRule();
diff --git a/ruta-core/src/main/java/org/apache/uima/ruta/rule/ComposedRuleElement.java b/ruta-core/src/main/java/org/apache/uima/ruta/rule/ComposedRuleElement.java
index e25038b..ade9153 100644
--- a/ruta-core/src/main/java/org/apache/uima/ruta/rule/ComposedRuleElement.java
+++ b/ruta-core/src/main/java/org/apache/uima/ruta/rule/ComposedRuleElement.java
@@ -527,7 +527,8 @@
containerMatch, sideStepOrigin, entryPoint, stream, crowd);
}
- if (sideStepOrigin != null && !failed) {
+ if (sideStepOrigin != null && !failed && sideStepOrigin.getContainer() != null) {
+ // only continue sidestep if we did not yet reach the root
return sideStepOrigin.continueSideStep(after, ruleMatch, ruleApply, containerMatch,
entryPoint, stream, crowd);
}
diff --git a/ruta-core/src/main/java/org/apache/uima/ruta/rule/RutaRuleElement.java b/ruta-core/src/main/java/org/apache/uima/ruta/rule/RutaRuleElement.java
index a438cca..d90a999 100644
--- a/ruta-core/src/main/java/org/apache/uima/ruta/rule/RutaRuleElement.java
+++ b/ruta-core/src/main/java/org/apache/uima/ruta/rule/RutaRuleElement.java
@@ -201,7 +201,9 @@
if (nextRuleElement != null) {
result = nextRuleElement.continueMatch(after, eachAnchor, extendedMatch, ruleApply,
extendedContainerMatch, sideStepOrigin, entryPoint, stream, crowd);
- } else if (sideStepOrigin != null && !failed) {
+ } else if (sideStepOrigin != null && !failed && containedIn(sideStepOrigin, getContainer())) {
+ // continue directly with the sidestep if it is contained in this container
+ // if not, we might miss matches in the same direction
result = sideStepOrigin.continueSideStep(after, extendedMatch, ruleApply,
extendedContainerMatch, entryPoint, stream, crowd);
} else if (getContainer() instanceof ComposedRuleElement) {
@@ -212,6 +214,26 @@
return result;
}
+ private boolean containedIn(RuleElement sideStepOrigin, RuleElementContainer container) {
+ // TODO: should we support this in interface?
+ if (container == null || sideStepOrigin == null) {
+ return false;
+ }
+ List<RuleElement> ruleElements = container.getRuleElements();
+ if (ruleElements.contains(sideStepOrigin)) {
+ return true;
+ } else {
+ for (RuleElement ruleElement : ruleElements) {
+ if (ruleElement instanceof RuleElementContainer) {
+ if (containedIn(sideStepOrigin, (RuleElementContainer) ruleElement)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
@Override
public List<RuleMatch> continueMatch(boolean after, AnnotationFS annotation, RuleMatch ruleMatch,
RuleApply ruleApply, ComposedRuleElementMatch containerMatch, RuleElement sideStepOrigin,
diff --git a/ruta-core/src/test/java/org/apache/uima/ruta/ConjunctiveRuleElementTest.java b/ruta-core/src/test/java/org/apache/uima/ruta/ConjunctiveRuleElementTest.java
index 02c7bff..0dd61f2 100644
--- a/ruta-core/src/test/java/org/apache/uima/ruta/ConjunctiveRuleElementTest.java
+++ b/ruta-core/src/test/java/org/apache/uima/ruta/ConjunctiveRuleElementTest.java
@@ -97,7 +97,7 @@
}
@Test
- public void testWithStartAnchor() {
+ public void testWithStartAnchor() throws Exception {
String document = "Peter did something.";
String script = "";
@@ -120,18 +120,12 @@
String fn3 = "tense";
list.add(new TestFeature(fn3, "", "uima.cas.String"));
- CAS cas = null;
- try {
- cas = RutaTestUtils.getCAS(document, typeMap, featureMap);
- Ruta.apply(cas, script);
- } catch (Exception e) {
- e.printStackTrace();
- }
+ CAS cas = RutaTestUtils.getCAS(document, typeMap, featureMap);
+ Ruta.apply(cas, script);
RutaTestUtils.assertAnnotationsEquals(cas, 1, 1, "Peter did");
RutaTestUtils.assertAnnotationsEquals(cas, 2, 1, "Peter did");
RutaTestUtils.assertAnnotationsEquals(cas, 3, 1, "did something");
- cas.release();
}
}
diff --git a/ruta-core/src/test/java/org/apache/uima/ruta/rule/ManualAnchoringTest.java b/ruta-core/src/test/java/org/apache/uima/ruta/rule/ManualAnchoringTest.java
index 64b7561..e7d4a6c 100644
--- a/ruta-core/src/test/java/org/apache/uima/ruta/rule/ManualAnchoringTest.java
+++ b/ruta-core/src/test/java/org/apache/uima/ruta/rule/ManualAnchoringTest.java
@@ -38,6 +38,31 @@
RutaTestUtils.assertAnnotationsEquals(cas, 3, 1, "A, B and C");
- cas.release();
}
+
+ @Test
+ public void testAcrossComposedInSequence() throws Exception {
+ String text = "bla CAP 1-2 bla";
+
+ String script = "FOREACH(cap) CAP{}{";
+ script += "ANY{-PARTOF(SPECIAL)} @cap (NUM SPECIAL NUM){-> T1} ANY{-PARTOF(SPECIAL)};";
+ script += "}";
+
+ CAS cas = RutaTestUtils.getCAS(text);
+ Ruta.apply(cas, script);
+
+ RutaTestUtils.assertAnnotationsEquals(cas, 1, 1, "1-2");
+ }
+
+ @Test
+ public void testLeaveComposedInSequence() throws Exception {
+ String text = "bla w CAP w bla";
+ String script = "(W @CAP W) {->T1} ANY{-PARTOF(NUM)};";
+
+ CAS cas = RutaTestUtils.getCAS(text);
+ Ruta.apply(cas, script);
+
+ RutaTestUtils.assertAnnotationsEquals(cas, 1, 1, "w CAP w");
+ }
+
}
diff --git a/ruta-core/src/test/java/org/apache/uima/ruta/rule/SidestepInComposedTest.java b/ruta-core/src/test/java/org/apache/uima/ruta/rule/SidestepInComposedTest.java
index c990a44..c97b34c 100644
--- a/ruta-core/src/test/java/org/apache/uima/ruta/rule/SidestepInComposedTest.java
+++ b/ruta-core/src/test/java/org/apache/uima/ruta/rule/SidestepInComposedTest.java
@@ -66,4 +66,18 @@
}
+ @Test
+ public void testOptionalBeforeComposed() throws Exception {
+ String document = "test 05/05 test\n";
+ document += "test 06/06 . test\n";
+ document += "test . 07/07 test\n";
+ String script = "_{-PARTOF(PM)} (NUM SPECIAL @NUM){-> T1} _{-PARTOF({PM})};\n";
+
+ CAS cas = RutaTestUtils.getCAS(document);
+ Ruta.apply(cas, script);
+
+ RutaTestUtils.assertAnnotationsEquals(cas, 1, 1, "05/05");
+
+ }
+
}
diff --git a/ruta-core/src/test/java/org/apache/uima/ruta/rule/quantifier/MinMaxQuantifierTest.java b/ruta-core/src/test/java/org/apache/uima/ruta/rule/quantifier/MinMaxQuantifierTest.java
index facef59..84a8086 100644
--- a/ruta-core/src/test/java/org/apache/uima/ruta/rule/quantifier/MinMaxQuantifierTest.java
+++ b/ruta-core/src/test/java/org/apache/uima/ruta/rule/quantifier/MinMaxQuantifierTest.java
@@ -49,10 +49,10 @@
CAS cas = RutaTestUtils.getCAS(document);
Ruta.apply(cas, script);
- if (RutaTestUtils.DEBUG_MODE) {
- RutaTestUtils.storeTypeSystem();
- RutaTestUtils.storeCas(cas, "testMinMaxOnComposedWithAnchor");
- }
+// if (RutaTestUtils.DEBUG_MODE) {
+// RutaTestUtils.storeTypeSystem();
+// RutaTestUtils.storeCas(cas, "testMinMaxOnComposedWithAnchor");
+// }
RutaTestUtils.assertAnnotationsEquals(cas, 1, 3, "1 2 3 4 5 6 7 8", "2 3 4 5 6 7 8 9",
"3 4 5 6 7 8 9 10");