Merge branch 'main' into bugfix/161-Right-to-left-wildcard-matches-too-much
diff --git a/ruta-core/src/main/antlr3/org/apache/uima/ruta/parser/RutaParser.g b/ruta-core/src/main/antlr3/org/apache/uima/ruta/parser/RutaParser.g
index 4627ba1..9bc7782 100644
--- a/ruta-core/src/main/antlr3/org/apache/uima/ruta/parser/RutaParser.g
+++ b/ruta-core/src/main/antlr3/org/apache/uima/ruta/parser/RutaParser.g
@@ -2431,7 +2431,7 @@
 	| match = dottedIdWithIndex2 (comp = LESS | comp = GREATER | comp = GREATEREQUAL | comp = LESSEQUAL |comp =  EQUAL | comp = NOTEQUAL) arg = argument

 	{MatchReference mr = expressionFactory.createMatchReference(match, comp, arg);

 	expr = expressionFactory.createAnnotationTypeExpression(mr);}

-        | (complexStringExpression) => cse = complexStringExpression {expr = cse;}

+        | (genericComposedExpression) => gce = genericComposedExpression {expr = gce;}

 	| (featureExpression)=> fe = featureExpression {expr = expressionFactory.createGenericFeatureExpression(fe);}

 	| a2 = booleanExpression {expr = a2;}

 	| a3 = numberExpression {expr = a3;}

@@ -2720,6 +2720,14 @@
 	;

 

 

+complexNumberExpression returns [INumberExpression expr = null]

+@init{List<INumberExpression> exprs = new ArrayList<INumberExpression>();

+	List<Token> ops = new ArrayList<Token>();}

+	:   

+	e = multiplicativeExpression{exprs.add(e);} ((PLUS | MINUS)=> op = (PLUS | MINUS){ops.add(op);} e = multiplicativeExpression{exprs.add(e);} )+

+	{expr = expressionFactory.createComposedNumberExpression(exprs,ops);}

+	;

+

 additiveExpression returns [INumberExpression expr = null]

 @init{List<INumberExpression> exprs = new ArrayList<INumberExpression>();

 	List<Token> ops = new ArrayList<Token>();}

@@ -2778,7 +2786,7 @@
 	|(e = stringFunction)=> e = stringFunction{expr = e;} 

 	;

 

-complexStringExpression returns [IStringExpression expr = null]

+genericComposedExpression returns [IRutaExpression expr = null]

 options {

 	backtrack = true;

 }

@@ -2786,7 +2794,7 @@
 	:

 	a1 = simpleArgument {list.add(a1);}

 	((PLUS)=>PLUS an = simpleArgument {list.add(an);})+

-	{expr = expressionFactory.createGenericComposedStringExpression(list);}

+	{expr = expressionFactory.createGenericComposedExpression(list);}

 	;

 

 

diff --git a/ruta-core/src/main/java/org/apache/uima/ruta/condition/ConditionFactory.java b/ruta-core/src/main/java/org/apache/uima/ruta/condition/ConditionFactory.java
index 3cde11f..e61c247 100644
--- a/ruta-core/src/main/java/org/apache/uima/ruta/condition/ConditionFactory.java
+++ b/ruta-core/src/main/java/org/apache/uima/ruta/condition/ConditionFactory.java
@@ -6,9 +6,9 @@
  * 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

diff --git a/ruta-core/src/main/java/org/apache/uima/ruta/expression/ExpressionFactory.java b/ruta-core/src/main/java/org/apache/uima/ruta/expression/ExpressionFactory.java
index cf752cf..e990b75 100644
--- a/ruta-core/src/main/java/org/apache/uima/ruta/expression/ExpressionFactory.java
+++ b/ruta-core/src/main/java/org/apache/uima/ruta/expression/ExpressionFactory.java
@@ -181,13 +181,15 @@
     for (IRutaExpression each : expressions) {

       if (each instanceof IStringExpression) {

         stringExpression.add((IStringExpression) each);

-      } else {

-        System.out.println();

       }

     }

     return new ComposedStringExpression(stringExpression);

   }

 

+  public IRutaExpression createGenericComposedExpression(List<IRutaExpression> list) {

+    return new GenericComposedExpression(list);

+  }

+

   public AbstractStringExpression createReferenceStringExpression(Token var) {

     return new StringVariableExpression(var.getText());

   }

diff --git a/ruta-core/src/main/java/org/apache/uima/ruta/expression/GenericComposedExpression.java b/ruta-core/src/main/java/org/apache/uima/ruta/expression/GenericComposedExpression.java
new file mode 100644
index 0000000..d9c007a
--- /dev/null
+++ b/ruta-core/src/main/java/org/apache/uima/ruta/expression/GenericComposedExpression.java
@@ -0,0 +1,83 @@
+/*
+ * 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;
+
+import java.util.List;
+
+import org.apache.uima.ruta.RutaStream;
+import org.apache.uima.ruta.expression.number.INumberExpression;
+import org.apache.uima.ruta.expression.string.IStringExpression;
+import org.apache.uima.ruta.rule.MatchContext;
+
+public class GenericComposedExpression extends RutaExpression implements INumberExpression {
+
+  private final List<IRutaExpression> expressions;
+
+  public GenericComposedExpression(List<IRutaExpression> expressions) {
+    super();
+    this.expressions = expressions;
+  }
+
+  @Override
+  public String getStringValue(MatchContext context, RutaStream stream) {
+    if (expressions == null) {
+      return null;
+    }
+    if (expressions.size() == 1) {
+      IRutaExpression first = expressions.get(0);
+      if (first instanceof IStringExpression) {
+        return ((IStringExpression) first).getStringValue(context, stream);
+      }
+      return null;
+    }
+    StringBuilder result = new StringBuilder();
+    for (IRutaExpression each : expressions) {
+      if (each instanceof IStringExpression) {
+        result.append(((IStringExpression) each).getStringValue(context, stream));
+      }
+    }
+    return result.toString();
+  }
+
+  @Override
+  public int getIntegerValue(MatchContext context, RutaStream stream) {
+    return (int) getDoubleValue(context, stream);
+  }
+
+  @Override
+  public double getDoubleValue(MatchContext context, RutaStream stream) {
+    double result = 0;
+    for (IRutaExpression each : expressions) {
+      if (each instanceof INumberExpression) {
+        result += ((INumberExpression) each).getDoubleValue(context, stream);
+      }
+    }
+    return result;
+  }
+
+  @Override
+  public float getFloatValue(MatchContext context, RutaStream stream) {
+    return (float) getDoubleValue(context, stream);
+  }
+
+  public List<IRutaExpression> getExpressions() {
+    return expressions;
+  }
+}
diff --git a/ruta-core/src/main/java/org/apache/uima/ruta/utils/UIMAUtils.java b/ruta-core/src/main/java/org/apache/uima/ruta/utils/UIMAUtils.java
index cac0a9a..5a3bccc 100644
--- a/ruta-core/src/main/java/org/apache/uima/ruta/utils/UIMAUtils.java
+++ b/ruta-core/src/main/java/org/apache/uima/ruta/utils/UIMAUtils.java
@@ -6,9 +6,9 @@
  * 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

@@ -33,7 +33,7 @@
 

   public static FSArray<? extends FeatureStructure> toFSArray(JCas jCas,

           List<? extends FeatureStructure> fsList) {

-    FSArray<FeatureStructure> fsArray = new FSArray<FeatureStructure>(jCas, fsList.size());

+    FSArray<FeatureStructure> fsArray = new FSArray<>(jCas, fsList.size());

     fsArray.copyFromArray(fsList.toArray(new FeatureStructure[fsList.size()]), 0, 0, fsList.size());

     return fsArray;

   }

@@ -58,7 +58,7 @@
 

   public static <T extends FeatureStructure> List<T> toList(FSArray<FeatureStructure> fsArray,

           Class<T> cls) {

-    List<T> list = new ArrayList<T>();

+    List<T> list = new ArrayList<>();

     if (fsArray == null) {

       return list;

     }

diff --git a/ruta-core/src/main/java/org/apache/uima/ruta/verbalize/ExpressionVerbalizer.java b/ruta-core/src/main/java/org/apache/uima/ruta/verbalize/ExpressionVerbalizer.java
index 2e8cba7..c4b3a94 100644
--- a/ruta-core/src/main/java/org/apache/uima/ruta/verbalize/ExpressionVerbalizer.java
+++ b/ruta-core/src/main/java/org/apache/uima/ruta/verbalize/ExpressionVerbalizer.java
@@ -6,9 +6,9 @@
  * 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

@@ -20,10 +20,13 @@
 package org.apache.uima.ruta.verbalize;

 

 import java.util.Iterator;

+import java.util.List;

+import java.util.stream.Collectors;

 

 import org.apache.commons.lang3.StringUtils;

 import org.apache.uima.cas.CAS;

 import org.apache.uima.ruta.expression.AnnotationTypeExpression;

+import org.apache.uima.ruta.expression.GenericComposedExpression;

 import org.apache.uima.ruta.expression.IRutaExpression;

 import org.apache.uima.ruta.expression.MatchReference;

 import org.apache.uima.ruta.expression.NullExpression;

@@ -83,6 +86,8 @@
   public String verbalize(IRutaExpression expression) {

     if (expression instanceof NullExpression) {

       return "null";

+    } else if (expression instanceof GenericComposedExpression) {

+      return verbalize((GenericComposedExpression) expression);

     } else if (expression instanceof GenericFeatureExpression) {

       return verbalize(((GenericFeatureExpression) expression).getFeatureExpression());

     } else if (expression instanceof AnnotationTypeExpression) {

@@ -336,4 +341,9 @@
             + verbalize(expression.getArg());

   }

 

+  public String verbalize(GenericComposedExpression expression) {

+    GenericComposedExpression gce = expression;

+    List<IRutaExpression> expressions = gce.getExpressions();

+    return expressions.stream().map(e -> verbalize(e)).collect(Collectors.joining("+"));

+  }

 }

diff --git a/ruta-core/src/test/java/org/apache/uima/ruta/expression/number/ComposedNumberExpressionTest.java b/ruta-core/src/test/java/org/apache/uima/ruta/expression/number/ComposedNumberExpressionTest.java
index 7c8f79e..73e93d0 100644
--- a/ruta-core/src/test/java/org/apache/uima/ruta/expression/number/ComposedNumberExpressionTest.java
+++ b/ruta-core/src/test/java/org/apache/uima/ruta/expression/number/ComposedNumberExpressionTest.java
@@ -6,9 +6,9 @@
  * 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
@@ -31,7 +31,7 @@
   public void testGetStringValueWithInteger() {
     List<INumberExpression> list = new ArrayList<>();
     list.add(new SimpleNumberExpression(Integer.valueOf(1)));
-    ComposedNumberExpression expr = new ComposedNumberExpression(list, new ArrayList<String>());
+    ComposedNumberExpression expr = new ComposedNumberExpression(list, new ArrayList<>());
     String string = expr.getStringValue(null, null);
     assertThat(string).isEqualTo("1");
   }
diff --git a/ruta-core/src/test/java/org/apache/uima/ruta/expression/number/NumberExpressionTest.java b/ruta-core/src/test/java/org/apache/uima/ruta/expression/number/NumberExpressionTest.java
new file mode 100644
index 0000000..76c3855
--- /dev/null
+++ b/ruta-core/src/test/java/org/apache/uima/ruta/expression/number/NumberExpressionTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.number;
+
+import org.apache.uima.cas.CAS;
+import org.apache.uima.ruta.engine.Ruta;
+import org.apache.uima.ruta.engine.RutaTestUtils;
+import org.junit.jupiter.api.Test;
+
+public class NumberExpressionTest {
+
+  @Test
+  public void testComposedAssignment() throws Exception {
+    String script = "INT i = 1;";
+    script += "Document{i==2 -> T1};";
+    script += "Document{-> i = i + 1};";
+    script += "Document{i==2 -> T2};";
+    script += "Document{-> i = 1 + 1 + 1};";
+    script += "Document{i==3 -> T3};";
+    script += "Document{i== 1+1+1 -> T4};";
+    script += "Document{i== 1+i-1 -> T5};";
+    script += "Document{-> i = (1 + 1 * 3) / 2};";
+    script += "Document{i== 8 / 4 -> T6};";
+    CAS cas = RutaTestUtils.getCAS("This is a test.");
+    Ruta.apply(cas, script);
+
+    RutaTestUtils.assertAnnotationsEquals(cas, 1, 0);
+    RutaTestUtils.assertAnnotationsEquals(cas, 2, 1, "This is a test.");
+    RutaTestUtils.assertAnnotationsEquals(cas, 3, 1, "This is a test.");
+    RutaTestUtils.assertAnnotationsEquals(cas, 4, 1, "This is a test.");
+    RutaTestUtils.assertAnnotationsEquals(cas, 5, 1, "This is a test.");
+    RutaTestUtils.assertAnnotationsEquals(cas, 6, 1, "This is a test.");
+  }
+
+}
diff --git a/ruta-core/src/test/java/org/apache/uima/ruta/verbalizer/ExpressionVerbalizerTest.java b/ruta-core/src/test/java/org/apache/uima/ruta/verbalizer/ExpressionVerbalizerTest.java
index 6e8c541..8ad8681 100644
--- a/ruta-core/src/test/java/org/apache/uima/ruta/verbalizer/ExpressionVerbalizerTest.java
+++ b/ruta-core/src/test/java/org/apache/uima/ruta/verbalizer/ExpressionVerbalizerTest.java
@@ -6,9 +6,9 @@
  * 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

@@ -22,8 +22,10 @@
 import static org.assertj.core.api.Assertions.assertThat;

 

 import java.util.ArrayList;

+import java.util.Arrays;

 import java.util.List;

 

+import org.apache.uima.ruta.expression.GenericComposedExpression;

 import org.apache.uima.ruta.expression.MatchReference;

 import org.apache.uima.ruta.expression.annotation.AnnotationLabelExpression;

 import org.apache.uima.ruta.expression.annotation.AnnotationVariableExpression;

@@ -67,49 +69,16 @@
   @Test

   public void test() {

     RutaVerbalizer v = new RutaVerbalizer();

-    // List<TypeExpression> typeExprList = new ArrayList<TypeExpression>();

-    // List<StringExpression> stringExprList = new ArrayList<StringExpression>();

-    // List<RutaExpression> exprList = new ArrayList<RutaExpression>();

-    // List<INumberExpression> indexes = new ArrayList<INumberExpression>();

-

-    //

-    // // typeExprList.add(typeExpr1);

-    // // typeExprList.add(typeExpr2);

-    //

-    //

-    // StringExpression stringExpr = new SimpleStringExpression("string");

-    // stringExprList.add(stringExpr);

-    // // exprList.add(typeExpr1);

-    // WordTableExpression wordTableExpr = new ReferenceWordTableExpression(var);

-    // WordListExpression wordListExpr = new ReferenceWordListExpression(var);

-    // TypeListExpression typeListExpr = new SimpleTypeListExpression(typeExprList);

-    // StringListExpression stringListExpr = new SimpleStringListExpression(stringExprList);

-    // Map<StringExpression, RutaExpression> stringExprMap = new HashMap<StringExpression,

-    // RutaExpression>();

-    // Map<StringExpression, INumberExpression> stringExprNumExprMap = new HashMap<StringExpression,

-    // INumberExpression>();

-    // Map<StringExpression, TypeExpression> stringExprTypeExprMap = new HashMap<StringExpression,

-    // TypeExpression>();

-    // @SuppressWarnings("rawtypes")

-    // ListExpression listExpr = new SimpleTypeListExpression(typeExprList);

-    // @SuppressWarnings("rawtypes")

-    // List<ListExpression> listExprList = new ArrayList<ListExpression>();

-    // listExprList.add(listExpr);

-    // stringExprMap.put(stringExpr, stringExpr);

-    // // stringExprNumExprMap.put(stringExpr, numExpr1);

-    // // stringExprTypeExprMap.put(stringExpr, typeExpr1);

-    // // indexes.add(numExpr1);

-    // // indexes.add(numExpr2);

 

     String s = null;

     String var = "anyVar";

     ITypeExpression typeExpr1 = new SimpleTypeExpression("Type1");

     ITypeExpression typeExpr2 = new TypeVariableExpression("typeVar");

 

-    List<INumberExpression> numExprList1 = new ArrayList<INumberExpression>();

-    List<INumberExpression> numExprList2 = new ArrayList<INumberExpression>();

-    List<String> opList1 = new ArrayList<String>();

-    List<String> opList2 = new ArrayList<String>();

+    List<INumberExpression> numExprList1 = new ArrayList<>();

+    List<INumberExpression> numExprList2 = new ArrayList<>();

+    List<String> opList1 = new ArrayList<>();

+    List<String> opList2 = new ArrayList<>();

     INumberExpression numExpr1 = new SimpleNumberExpression(4);

     INumberExpression numExpr2 = new NumberVariableExpression("numVar");

     INumberExpression numExpr3 = new NumberVariableExpression("4.9");

@@ -171,7 +140,7 @@
     s = v.verbalize(boolExpr11);

     assertThat(s).isEqualTo("Type1 != typeVar");

 

-    List<IStringExpression> stringExprList = new ArrayList<IStringExpression>();

+    List<IStringExpression> stringExprList = new ArrayList<>();

     AbstractStringExpression stringExpr1 = new SimpleStringExpression("string");

     AbstractStringExpression stringExpr2 = new StringVariableExpression(var);

     stringExprList.add(stringExpr1);

@@ -192,7 +161,7 @@
     s = v.verbalize(sle2);

     assertThat(s).isEqualTo("anyVar");

 

-    List<IBooleanExpression> boolExprList = new ArrayList<IBooleanExpression>();

+    List<IBooleanExpression> boolExprList = new ArrayList<>();

     boolExprList.add(boolExpr1);

     boolExprList.add(boolExpr3);

     AbstractBooleanListExpression ble1 = new SimpleBooleanListExpression(boolExprList);

@@ -202,7 +171,7 @@
     s = v.verbalize(ble2);

     assertThat(s).isEqualTo("anyVar");

 

-    List<INumberExpression> numExprList = new ArrayList<INumberExpression>();

+    List<INumberExpression> numExprList = new ArrayList<>();

     numExprList.add(numExpr1);

     numExprList.add(numExpr3);

     AbstractNumberListExpression nle1 = new SimpleNumberListExpression(numExprList);

@@ -212,7 +181,7 @@
     s = v.verbalize(nle2);

     assertThat(s).isEqualTo("anyVar");

 

-    List<ITypeExpression> typeExprList = new ArrayList<ITypeExpression>();

+    List<ITypeExpression> typeExprList = new ArrayList<>();

     typeExprList.add(typeExpr1);

     typeExprList.add(typeExpr2);

     AbstractTypeListExpression tle1 = new SimpleTypeListExpression(typeExprList);

@@ -243,4 +212,22 @@
     assertThat(v.verbalize(new GenericFeatureExpression(null))).isEqualTo("");

   }

 

+  @Test

+  public void testGenericComposedExpression() {

+    RutaVerbalizer v = new RutaVerbalizer();

+    assertThat(v.verbalize(new GenericComposedExpression(

+            Arrays.asList(new SimpleStringExpression("a"), new SimpleStringExpression("b")))))

+                    .isEqualTo("\"a\"+\"b\"");

+    assertThat(v.verbalize(new GenericComposedExpression(

+            Arrays.asList(new SimpleNumberExpression(1), new SimpleNumberExpression(2)))))

+                    .isEqualTo("1+2");

+    assertThat(v.verbalize(new GenericComposedExpression(

+            Arrays.asList(new SimpleStringExpression("a"), new SimpleNumberExpression(2)))))

+                    .isEqualTo("\"a\"+2");

+    assertThat(v.verbalize(new GenericComposedExpression(

+            Arrays.asList(new SimpleFeatureExpression(new MatchReference("abc.d")),

+                    new AnnotationVariableExpression("l"))))).isEqualTo("abc.d+l");

+    assertThat(v.verbalize(new GenericFeatureExpression(null))).isEqualTo("");

+  }

+

 }