This is an automated email from the ASF dual-hosted git repository.
pkluegl pushed a commit to branch
bugfix/159-Assignment-of-composed-number-expression-is-broken
in repository https://gitbox.apache.org/repos/asf/uima-ruta.git
The following commit(s) were added to
refs/heads/bugfix/159-Assignment-of-composed-number-expression-is-broken by
this push:
new 2343d2ea Issue #159: Assignment of composed number expression is broken
2343d2ea is described below
commit 2343d2ea4bd2cc600887b67104d3b4999aa7bd88
Author: kluegl <[email protected]>
AuthorDate: Fri Jan 26 15:24:22 2024 +0100
Issue #159: Assignment of composed number expression is broken
- avoid disambiguation problems with generic expression
---
.../org/apache/uima/ruta/parser/RutaParser.g | 14 +++-
.../uima/ruta/condition/ConditionFactory.java | 4 +-
.../uima/ruta/expression/ExpressionFactory.java | 6 +-
.../ruta/expression/GenericComposedExpression.java | 83 ++++++++++++++++++++++
.../java/org/apache/uima/ruta/utils/UIMAUtils.java | 8 +--
.../uima/ruta/verbalize/ExpressionVerbalizer.java | 14 +++-
.../number/ComposedNumberExpressionTest.java | 6 +-
.../expression/number/NumberExpressionTest.java | 51 +++++++++++++
.../ruta/verbalizer/ExpressionVerbalizerTest.java | 73 ++++++++-----------
9 files changed, 200 insertions(+), 59 deletions(-)
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 4627ba17..9bc7782f 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 @@ options {
| 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 @@ numberVariable returns [Token ref = null]
;
+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 @@ List<IStringExpression> exprs = new
ArrayList<IStringExpression>();
|(e = stringFunction)=> e = stringFunction{expr = e;}
;
-complexStringExpression returns [IStringExpression expr = null]
+genericComposedExpression returns [IRutaExpression expr = null]
options {
backtrack = true;
}
@@ -2786,7 +2794,7 @@ options {
:
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 3cde11fe..e61c2473 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 cf752cfc..e990b754 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 @@ public class ExpressionFactory {
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 00000000..d9c007a2
--- /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 cac0a9a6..5a3bccce 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 class UIMAUtils {
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 class UIMAUtils {
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 2e8cba7f..c4b3a947 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 class ExpressionVerbalizer {
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 @@ public class ExpressionVerbalizer {
+ 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 7c8f79e4..73e93d00 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 class ComposedNumberExpressionTest {
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 00000000..76c38550
--- /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 6e8c541c..8ad86819 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 @@ package org.apache.uima.ruta.verbalizer;
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 @@ public class ExpressionVerbalizerTest {
@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 @@ public class ExpressionVerbalizerTest {
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 @@ public class ExpressionVerbalizerTest {
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 @@ public class ExpressionVerbalizerTest {
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 @@ public class ExpressionVerbalizerTest {
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 @@ public class ExpressionVerbalizerTest {
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("");
+ }
+
}