Modified: 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/AbstractRutaAction.java
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/AbstractRutaAction.java?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/AbstractRutaAction.java
 (original)
+++ 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/AbstractRutaAction.java
 Tue Dec 18 19:52:18 2018
@@ -20,9 +20,13 @@
 package org.apache.uima.ruta.action;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
+import org.apache.commons.lang3.StringUtils;
+import org.apache.uima.cas.text.AnnotationFS;
 import org.apache.uima.ruta.RutaElement;
+import org.apache.uima.ruta.RutaEnvironment;
 import org.apache.uima.ruta.RutaStream;
 import org.apache.uima.ruta.expression.number.INumberExpression;
 import org.apache.uima.ruta.rule.MatchContext;
@@ -31,6 +35,8 @@ import org.apache.uima.ruta.visitor.Infe
 
 public abstract class AbstractRutaAction extends RutaElement {
 
+  private String label;
+
   public AbstractRutaAction() {
     super();
   }
@@ -42,7 +48,8 @@ public abstract class AbstractRutaAction
     return getClass().getSimpleName();
   }
 
-  protected List<Integer> getIndexList(List<INumberExpression> indexes, 
MatchContext context, RutaStream stream) {
+  protected List<Integer> getIndexList(List<INumberExpression> indexes, 
MatchContext context,
+          RutaStream stream) {
     RuleElement element = context.getElement();
     List<Integer> indexList = new ArrayList<Integer>();
     if (indexes == null || indexes.isEmpty()) {
@@ -61,5 +68,28 @@ public abstract class AbstractRutaAction
     }
     return indexList;
   }
-  
+
+  public void setLabel(String label) {
+    this.label = label;
+  }
+
+  public String getLabel() {
+    return this.label;
+  }
+
+  protected void addAnnotationToLabel(AnnotationFS annotation, MatchContext 
context) {
+    if (StringUtils.isBlank(label)) {
+      return;
+    }
+    RutaEnvironment environment = context.getParent().getEnvironment();
+
+    Class<?> variableType = environment.getVariableType(label);
+    if (List.class.equals(variableType)
+            && 
AnnotationFS.class.equals(environment.getVariableGenericType(label))) {
+      environment.setVariableValue(label, Arrays.asList(annotation));
+    } else if (AnnotationFS.class.equals(variableType)) {
+      environment.setVariableValue(label, annotation);
+    }
+  }
+
 }

Modified: 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/CreateAction.java
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/CreateAction.java?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/CreateAction.java
 (original)
+++ 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/CreateAction.java
 Tue Dec 18 19:52:18 2018
@@ -73,6 +73,7 @@ public class CreateAction extends Abstra
         context.setAnnotation(matchedAnnotation);
         stream.assignFeatureValues(annotation, features, context);
         stream.addAnnotation(a, true, match);
+        addAnnotationToLabel(a, context);
       }
     }
   }

Modified: 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/FillAction.java
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/FillAction.java?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/FillAction.java
 (original)
+++ 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/FillAction.java
 Tue Dec 18 19:52:18 2018
@@ -39,7 +39,8 @@ public class FillAction extends Abstract
 
   private ITypeExpression structureType;
 
-  public FillAction(ITypeExpression structureType, Map<IStringExpression, 
IRutaExpression> features) {
+  public FillAction(ITypeExpression structureType,
+          Map<IStringExpression, IRutaExpression> features) {
     super();
     this.structureType = structureType;
     this.features = features;
@@ -77,6 +78,7 @@ public class FillAction extends Abstract
         context.setAnnotation(matchedAnnotation);
         stream.assignFeatureValues(annotationFS, features, context);
         stream.getCas().addFsToIndexes(annotationFS);
+        addAnnotationToLabel(annotationFS, context);
       }
     }
 

Modified: 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/GatherAction.java
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/GatherAction.java?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/GatherAction.java
 (original)
+++ 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/GatherAction.java
 Tue Dec 18 19:52:18 2018
@@ -85,6 +85,7 @@ public class GatherAction extends Abstra
         a.setBegin(matchedAnnotation.getBegin());
         a.setEnd(matchedAnnotation.getEnd());
         stream.addAnnotation(a, match);
+        addAnnotationToLabel(a, context);
       }
       TOP newStructure = null;
       if (newFS instanceof TOP) {
@@ -144,8 +145,8 @@ public class GatherAction extends Abstra
               // search for
               Collection<AnnotationFS> beginAnchors = 
stream.getBeginAnchor(fs.getBegin())
                       .getBeginAnchors(range);
-              Collection<AnnotationFS> endAnchors = 
stream.getEndAnchor(fs.getEnd()).getEndAnchors(
-                      range);
+              Collection<AnnotationFS> endAnchors = 
stream.getEndAnchor(fs.getEnd())
+                      .getEndAnchors(range);
               @SuppressWarnings("unchecked")
               Collection<AnnotationFS> intersection = 
CollectionUtils.intersection(beginAnchors,
                       endAnchors);
@@ -209,7 +210,6 @@ public class GatherAction extends Abstra
     return result;
   }
 
-
   public ITypeExpression getStructureType() {
     return structureType;
   }

Modified: 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/ShiftAction.java
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/ShiftAction.java?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/ShiftAction.java
 (original)
+++ 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/ShiftAction.java
 Tue Dec 18 19:52:18 2018
@@ -52,7 +52,7 @@ public class ShiftAction extends MarkAct
     RuleMatch match = context.getRuleMatch();
     RuleElement element = context.getElement();
     Type targetType = type.getType(context, stream);
-    
+
     List<Integer> indexList = getIndexList(context, list, stream);
     List<AnnotationFS> destinationAnnotationSpans = 
match.getMatchedAnnotations(indexList,
             element.getContainer());
@@ -62,7 +62,7 @@ public class ShiftAction extends MarkAct
             destinationAnnotationSpans.size());
 
     boolean expandAll = all == null ? false : all.getBooleanValue(context, 
stream);
-    
+
     RutaBasic firstBasicOfAll = stream.getFirstBasicOfAll();
     RutaBasic lastBasicOfAll = stream.getLastBasicOfAll();
     int windowBegin = firstBasicOfAll == null ? 0 : firstBasicOfAll.getBegin();
@@ -72,22 +72,23 @@ public class ShiftAction extends MarkAct
       AnnotationFS eachDestination = destinationAnnotationSpans.get(i);
       Set<AnnotationFS> allAnchoredAnnotations = new TreeSet<AnnotationFS>(
               new AnnotationComparator());
-      
-      if(expandAll) {
-      Collection<AnnotationFS> beginAnchors = 
stream.getBeginAnchor(eachMatched.getBegin())
-              .getBeginAnchors(targetType);
-      Collection<AnnotationFS> endAnchors = 
stream.getEndAnchor(eachMatched.getEnd())
-              .getEndAnchors(targetType);
-      allAnchoredAnnotations.addAll(beginAnchors);
-      allAnchoredAnnotations.addAll(endAnchors);
+
+      if (expandAll) {
+        Collection<AnnotationFS> beginAnchors = 
stream.getBeginAnchor(eachMatched.getBegin())
+                .getBeginAnchors(targetType);
+        Collection<AnnotationFS> endAnchors = 
stream.getEndAnchor(eachMatched.getEnd())
+                .getEndAnchors(targetType);
+        allAnchoredAnnotations.addAll(beginAnchors);
+        allAnchoredAnnotations.addAll(endAnchors);
       } else {
         
allAnchoredAnnotations.addAll(stream.getBestGuessedAnnotationsAt(eachMatched, 
targetType));
       }
-      
+
       for (AnnotationFS eachAnchored : allAnchoredAnnotations) {
         if (eachAnchored.getBegin() >= windowBegin && eachAnchored.getEnd() <= 
windowEnd) {
           Annotation a = (Annotation) eachAnchored;
           stream.changeOffsets(a, eachDestination.getBegin(), 
eachDestination.getEnd(), match);
+          addAnnotationToLabel(a, context);
         }
       }
     }

Modified: 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/TransferAction.java
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/TransferAction.java?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/TransferAction.java
 (original)
+++ 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/TransferAction.java
 Tue Dec 18 19:52:18 2018
@@ -55,6 +55,7 @@ public class TransferAction extends Type
           copyFeatures(annotationFS, createFS, cas);
           if (createFS instanceof AnnotationFS) {
             stream.addAnnotation((AnnotationFS) createFS, match);
+            addAnnotationToLabel((AnnotationFS) createFS, context);
           }
           cas.addFsToIndexes(createFS);
         }

Modified: 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/UnmarkAction.java
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/UnmarkAction.java?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/UnmarkAction.java
 (original)
+++ 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/action/UnmarkAction.java
 Tue Dec 18 19:52:18 2018
@@ -62,14 +62,17 @@ public class UnmarkAction extends TypeSe
 
     if (expression != null) {
       List<AnnotationFS> annotationList = 
expression.getAnnotationList(context, stream);
-      if(expression.getTypeExpression() != null && 
expression.getFeatureExpression() == null && 
expression.getAnnotationExpression() == null && 
expression.getAnnotationListExpression()== null) {
+      if (expression.getTypeExpression() != null && 
expression.getFeatureExpression() == null
+              && expression.getAnnotationExpression() == null
+              && expression.getAnnotationListExpression() == null) {
         // type-based like old behavior
         Type t = expression.getTypeExpression().getType(context, stream);
         removeTypeBased(context, stream, match, element, t);
       } else {
-      for (AnnotationFS annotationFS : annotationList) {
-        stream.removeAnnotation(annotationFS);
-      }
+        for (AnnotationFS annotationFS : annotationList) {
+          stream.removeAnnotation(annotationFS);
+          addAnnotationToLabel(annotationFS, context);
+        }
       }
     } else {
       Type t = type.getType(context, stream);
@@ -91,6 +94,7 @@ public class UnmarkAction extends TypeSe
       boolean subsumes = stream.getCas().getTypeSystem().subsumes(t, 
matchedType);
       if (subsumes && !allAtAnchor) {
         stream.removeAnnotation(annotationFS, matchedType);
+        addAnnotationToLabel(annotationFS, context);
       } else {
         RutaBasic beginAnchor = stream.getBeginAnchor(annotationFS.getBegin());
         Collection<AnnotationFS> beginAnchors = beginAnchor.getBeginAnchors(t);
@@ -98,6 +102,7 @@ public class UnmarkAction extends TypeSe
           for (AnnotationFS each : new ArrayList<AnnotationFS>(beginAnchors)) {
             if (allAtAnchor || each.getEnd() == annotationFS.getEnd()) {
               stream.removeAnnotation(each, t);
+              addAnnotationToLabel(annotationFS, context);
             }
           }
         }

Modified: 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/condition/PartOfCondition.java
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/condition/PartOfCondition.java?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/condition/PartOfCondition.java
 (original)
+++ 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/condition/PartOfCondition.java
 Tue Dec 18 19:52:18 2018
@@ -65,6 +65,9 @@ public class PartOfCondition extends Typ
   }
 
   private boolean check(Type t, AnnotationFS annotation, RuleElement element, 
RutaStream stream) {
+    if (annotation == null) {
+      return false;
+    }
     RutaBasic beginAnchor = stream.getBeginAnchor(annotation.getBegin());
     if (beginAnchor != null && beginAnchor.isPartOf(t)) {
       return true;

Modified: 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/condition/ScoreCondition.java
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/condition/ScoreCondition.java?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/condition/ScoreCondition.java
 (original)
+++ 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/condition/ScoreCondition.java
 Tue Dec 18 19:52:18 2018
@@ -19,11 +19,7 @@
 
 package org.apache.uima.ruta.condition;
 
-import java.util.List;
-
-import org.apache.uima.cas.Type;
 import org.apache.uima.cas.text.AnnotationFS;
-import org.apache.uima.jcas.tcas.Annotation;
 import org.apache.uima.ruta.RutaStream;
 import org.apache.uima.ruta.expression.number.INumberExpression;
 import org.apache.uima.ruta.expression.number.SimpleNumberExpression;
@@ -52,8 +48,8 @@ public class ScoreCondition extends Term
     AnnotationFS annotation = context.getAnnotation();
     RuleElement element = context.getElement();
     double score = 0;
-    RutaAnnotation rutaAnnotation =  stream.getRutaAnnotationFor(annotation, 
false, stream);
-    if(rutaAnnotation != null) {
+    RutaAnnotation rutaAnnotation = stream.getRutaAnnotationFor(annotation, 
false, stream);
+    if (rutaAnnotation != null) {
       score = rutaAnnotation.getScore();
     }
     if (var != null) {

Modified: 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/expression/AnnotationTypeExpression.java
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/expression/AnnotationTypeExpression.java?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/expression/AnnotationTypeExpression.java
 (original)
+++ 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/expression/AnnotationTypeExpression.java
 Tue Dec 18 19:52:18 2018
@@ -21,7 +21,6 @@ package org.apache.uima.ruta.expression;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
 
 import org.apache.uima.cas.FeatureStructure;
@@ -33,6 +32,7 @@ import org.apache.uima.ruta.expression.a
 import org.apache.uima.ruta.expression.feature.FeatureExpression;
 import org.apache.uima.ruta.expression.feature.FeatureMatchExpression;
 import org.apache.uima.ruta.expression.type.ITypeExpression;
+import org.apache.uima.ruta.expression.type.ITypeListExpression;
 import org.apache.uima.ruta.rule.MatchContext;
 
 public class AnnotationTypeExpression extends RutaExpression
@@ -42,6 +42,8 @@ public class AnnotationTypeExpression ex
 
   private ITypeExpression typeExpression;
 
+  private ITypeListExpression typeListExpression;
+
   private IAnnotationExpression annotationExpression;
 
   private IAnnotationListExpression annotationListExpression;
@@ -60,6 +62,7 @@ public class AnnotationTypeExpression ex
     annotationListExpression = reference.getAnnotationListExpression(context, 
stream);
     featureExpression = reference.getFeatureExpression(context, stream);
     typeExpression = reference.getTypeExpression(context, stream);
+    typeListExpression = reference.getTypeListExpression(context, stream);
     initialized = true;
   }
 
@@ -101,37 +104,48 @@ public class AnnotationTypeExpression ex
         }
       }
     } else {
-      Type type = getType(context, stream);
-      if (type != null) {
 
-        List<AnnotationFS> bestGuessedAnnotations = null;
+      List<Type> types = null;
+      if (typeListExpression != null) {
+        types = typeListExpression.getTypeList(context, stream);
+      } else {
+        Type type = getType(context, stream);
+        types = new ArrayList<>(1);
+        types.add(type);
+      }
+      for (Type type : types) {
 
-        if (featureExpression instanceof FeatureMatchExpression) {
-          // allow more matches for feature matches
-          bestGuessedAnnotations = stream.getAnnotationsByTypeInContext(type, 
context);
-        } else if (featureExpression != null) {
-          bestGuessedAnnotations = 
stream.getBestGuessedAnnotationsAt(context.getAnnotation(),
-                  type);
-        } else {
-          bestGuessedAnnotations = 
stream.getBestGuessedAnnotationsAt(context.getAnnotation(),
-                  type);
-          if (bestGuessedAnnotations.isEmpty()) {
-            bestGuessedAnnotations = new ArrayList<>(1);
-            
bestGuessedAnnotations.add(stream.getSingleAnnotationByTypeInContext(type, 
context));
+        if (type != null) {
+
+          List<AnnotationFS> bestGuessedAnnotations = null;
+
+          if (featureExpression instanceof FeatureMatchExpression) {
+            // allow more matches for feature matches
+            bestGuessedAnnotations = 
stream.getAnnotationsByTypeInContext(type, context);
+          } else if (featureExpression != null) {
+            bestGuessedAnnotations = 
stream.getBestGuessedAnnotationsAt(context.getAnnotation(),
+                    type);
+          } else {
+            bestGuessedAnnotations = 
stream.getBestGuessedAnnotationsAt(context.getAnnotation(),
+                    type);
+            if (bestGuessedAnnotations.isEmpty()) {
+              bestGuessedAnnotations = new ArrayList<>(1);
+              
bestGuessedAnnotations.add(stream.getSingleAnnotationByTypeInContext(type, 
context));
+            }
           }
-        }
 
-        if (featureExpression != null) {
-          Collection<AnnotationFS> annotations = new ArrayList<>();
-          annotations.addAll(bestGuessedAnnotations);
-          Collection<? extends AnnotationFS> featureAnnotations = 
featureExpression
-                  .getAnnotations(annotations, true, context, stream);
-          if (featureAnnotations != null && !featureAnnotations.isEmpty()) {
-            return featureAnnotations.iterator().next();
+          if (featureExpression != null) {
+            Collection<AnnotationFS> annotations = new ArrayList<>();
+            annotations.addAll(bestGuessedAnnotations);
+            Collection<? extends AnnotationFS> featureAnnotations = 
featureExpression
+                    .getAnnotations(annotations, true, context, stream);
+            if (featureAnnotations != null && !featureAnnotations.isEmpty()) {
+              return featureAnnotations.iterator().next();
+            }
+          }
+          if (bestGuessedAnnotations != null && 
!bestGuessedAnnotations.isEmpty()) {
+            return bestGuessedAnnotations.get(0);
           }
-        }
-        if (bestGuessedAnnotations != null && 
!bestGuessedAnnotations.isEmpty()) {
-          return bestGuessedAnnotations.get(0);
         }
       }
     }
@@ -171,6 +185,8 @@ public class AnnotationTypeExpression ex
       return annotationListExpression.getStringValue(context, stream);
     } else if (annotationExpression != null) {
       return annotationExpression.getStringValue(context, stream);
+    } else if (typeListExpression != null) {
+      return typeListExpression.getStringValue(context, stream);
     } else if (typeExpression != null) {
       return typeExpression.getStringValue(context, stream);
     }
@@ -199,39 +215,51 @@ public class AnnotationTypeExpression ex
         return result;
       }
     } else {
-      Type type = getType(context, stream);
-      if (type != null) {
 
-        List<AnnotationFS> bestGuessedAnnotations = null;
+      List<Type> types = null;
+      if (typeListExpression != null) {
+        types = typeListExpression.getTypeList(context, stream);
+      } else {
+        Type type = getType(context, stream);
+        types = new ArrayList<>(1);
+        types.add(type);
+      }
+
+      List<AnnotationFS> annotations = new ArrayList<>();
+
+      for (Type type : types) {
+        if (type != null) {
 
-        if (featureExpression instanceof FeatureMatchExpression) {
-          // allow more matches for feature matches
-          bestGuessedAnnotations = stream.getAnnotationsByTypeInContext(type, 
context);
-        } else if (featureExpression != null) {
-          bestGuessedAnnotations = 
stream.getBestGuessedAnnotationsAt(context.getAnnotation(),
-                  type);
-          if (bestGuessedAnnotations.isEmpty()) {
+          List<AnnotationFS> bestGuessedAnnotations = null;
+
+          if (featureExpression instanceof FeatureMatchExpression) {
+            // allow more matches for feature matches
+            bestGuessedAnnotations = 
stream.getAnnotationsByTypeInContext(type, context);
+          } else if (featureExpression != null) {
+            bestGuessedAnnotations = 
stream.getBestGuessedAnnotationsAt(context.getAnnotation(),
+                    type);
+            if (bestGuessedAnnotations.isEmpty()) {
+              bestGuessedAnnotations = 
stream.getAnnotationsByTypeInContext(type, context);
+            }
+          } else {
             bestGuessedAnnotations = 
stream.getAnnotationsByTypeInContext(type, context);
           }
-        } else {
-          bestGuessedAnnotations = stream.getAnnotationsByTypeInContext(type, 
context);
-        }
 
-        if (featureExpression != null) {
-          Collection<AnnotationFS> annotations = new ArrayList<>();
-          annotations.addAll(bestGuessedAnnotations);
-          Collection<? extends AnnotationFS> featureAnnotations = 
featureExpression
-                  .getAnnotations(annotations, true, context, stream);
-          if (featureAnnotations != null && !featureAnnotations.isEmpty()) {
-            return new ArrayList<>(featureAnnotations);
+          if (featureExpression != null) {
+            Collection<? extends AnnotationFS> featureAnnotations = 
featureExpression
+                    .getAnnotations(bestGuessedAnnotations, true, context, 
stream);
+            if (featureAnnotations != null && !featureAnnotations.isEmpty()) {
+              annotations.addAll(featureAnnotations);
+            }
+          }
+          if (bestGuessedAnnotations != null && 
!bestGuessedAnnotations.isEmpty()) {
+            annotations.addAll(bestGuessedAnnotations);
           }
-        }
-        if (bestGuessedAnnotations != null && 
!bestGuessedAnnotations.isEmpty()) {
-          return bestGuessedAnnotations;
         }
       }
+
+      return annotations;
     }
-    return Collections.emptyList();
   }
 
   public MatchReference getReference() {
@@ -246,6 +274,10 @@ public class AnnotationTypeExpression ex
     return typeExpression;
   }
 
+  public ITypeListExpression getTypeListExpression() {
+    return typeListExpression;
+  }
+
   public IAnnotationExpression getAnnotationExpression() {
     return annotationExpression;
   }
@@ -258,6 +290,7 @@ public class AnnotationTypeExpression ex
     return featureExpression;
   }
 
+  @Override
   public String toString() {
     return reference.toString();
   }

Modified: 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/expression/MatchReference.java
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/expression/MatchReference.java?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/expression/MatchReference.java
 (original)
+++ 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/expression/MatchReference.java
 Tue Dec 18 19:52:18 2018
@@ -36,7 +36,9 @@ import org.apache.uima.ruta.expression.f
 import org.apache.uima.ruta.expression.feature.FeatureMatchExpression;
 import org.apache.uima.ruta.expression.feature.SimpleFeatureExpression;
 import org.apache.uima.ruta.expression.type.ITypeExpression;
+import org.apache.uima.ruta.expression.type.ITypeListExpression;
 import org.apache.uima.ruta.expression.type.SimpleTypeExpression;
+import org.apache.uima.ruta.expression.type.TypeListVariableExpression;
 import org.apache.uima.ruta.expression.type.TypeVariableExpression;
 import org.apache.uima.ruta.rule.MatchContext;
 import org.apache.uima.ruta.utils.IndexedReference;
@@ -52,6 +54,8 @@ public class MatchReference extends Ruta
 
   private ITypeExpression typeExpression;
 
+  private ITypeListExpression typeListExpression;
+
   private IAnnotationExpression annotationExpression;
 
   private IAnnotationListExpression annotationListExpression;
@@ -79,6 +83,12 @@ public class MatchReference extends Ruta
     initialized = true;
   }
 
+  public MatchReference(ITypeListExpression expression) {
+    super();
+    this.typeListExpression = expression;
+    initialized = true;
+  }
+
   public MatchReference(IAnnotationExpression expression) {
     super();
     this.annotationExpression = expression;
@@ -127,11 +137,10 @@ public class MatchReference extends Ruta
       }
     }
     initialized = true;
-    if (typeExpression == null && annotationExpression == null
+    if (typeExpression == null && typeListExpression == null && 
annotationExpression == null
             && annotationListExpression == null) {
-      throw new IllegalArgumentException(
-              "Not able to resolve annotation/type expression: " + reference + 
-              " in script " +context.getParent().getName());
+      throw new IllegalArgumentException("Not able to resolve annotation/type 
expression: "
+              + reference + " in script " + context.getParent().getName());
     }
   }
 
@@ -150,6 +159,9 @@ public class MatchReference extends Ruta
       if (environment.isVariableOfType(candidate, 
RutaConstants.RUTA_VARIABLE_TYPE)) {
         typeExpression = new TypeVariableExpression(candidate);
         return true;
+      } else if (environment.isVariableOfType(candidate, 
RutaConstants.RUTA_VARIABLE_TYPE_LIST)) {
+        typeListExpression = new TypeListVariableExpression(candidate);
+        return true;
       } else if (environment.isVariableOfType(candidate, 
RutaConstants.RUTA_VARIABLE_ANNOTATION)) {
         annotationExpression = new AnnotationVariableExpression(candidate);
         return true;
@@ -170,6 +182,11 @@ public class MatchReference extends Ruta
     return typeExpression;
   }
 
+  public ITypeListExpression getTypeListExpression(MatchContext context, 
RutaStream stream) {
+    resolve(context, stream);
+    return typeListExpression;
+  }
+
   public IAnnotationExpression getAnnotationExpression(MatchContext context, 
RutaStream stream) {
     resolve(context, stream);
     return annotationExpression;
@@ -197,6 +214,9 @@ public class MatchReference extends Ruta
     if (typeExpression != null) {
       return typeExpression.toString();
     }
+    if (typeListExpression != null) {
+      return typeListExpression.toString();
+    }
     if (annotationExpression != null) {
       return annotationExpression.toString();
     }

Modified: 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/expression/type/ITypeListExpression.java
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/expression/type/ITypeListExpression.java?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/expression/type/ITypeListExpression.java
 (original)
+++ 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/expression/type/ITypeListExpression.java
 Tue Dec 18 19:52:18 2018
@@ -23,10 +23,11 @@ import java.util.List;
 
 import org.apache.uima.cas.Type;
 import org.apache.uima.ruta.RutaStream;
+import org.apache.uima.ruta.expression.string.IStringExpression;
 import org.apache.uima.ruta.rule.MatchContext;
 
-public interface ITypeListExpression {
-  
+public interface ITypeListExpression extends IStringExpression {
+
   List<Type> getTypeList(MatchContext context, RutaStream stream);
-  
+
 }

Modified: 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/rule/AbstractRuleElement.java
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/rule/AbstractRuleElement.java?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/rule/AbstractRuleElement.java
 (original)
+++ 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/rule/AbstractRuleElement.java
 Tue Dec 18 19:52:18 2018
@@ -210,7 +210,12 @@ public abstract class AbstractRuleElemen
     return false;
   }
 
-  private boolean isAlreadyCovered(AnnotationFS eachAnchor, RuleApply 
ruleApply, RutaStream stream) {
+  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) {

Modified: 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/rule/RutaAnnotationTypeMatcher.java
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/rule/RutaAnnotationTypeMatcher.java?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/rule/RutaAnnotationTypeMatcher.java
 (original)
+++ 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/rule/RutaAnnotationTypeMatcher.java
 Tue Dec 18 19:52:18 2018
@@ -59,24 +59,37 @@ public class RutaAnnotationTypeMatcher i
     } else if (expression.getAnnotationListExpression() != null) {
       result = expression.getAnnotationList(context, stream);
     } else {
-      Type type = getType(parent, stream);
-      if (type == null) {
-        return Collections.emptyList();
-      }
-      Type overallDAType = stream.getCas().getDocumentAnnotation().getType();
-      String name = type.getName();
-      if (StringUtils.equals(CAS.TYPE_NAME_DOCUMENT_ANNOTATION, name)
-              || "org.apache.uima.ruta.type.Document".equals(name) || 
overallDAType.equals(type)) {
-        // TODO what about dynamic windowing?
-        result = new ArrayList<>(1);
-        result.add(stream.getDocumentAnnotation());
+
+      // TODO defer to getter of expression?
+      List<Type> types = null;
+      if (expression.getTypeListExpression() != null) {
+        types = expression.getTypeListExpression().getTypeList(context, 
stream);
       } else {
-        result = stream.getAnnotations(type);
-      }
-      if (expression.getFeatureExpression() != null) {
-        return expression.getFeatureExpression().getAnnotations(result, 
CHECK_ON_FEATURE, context,
-                stream);
+        Type type = getType(context.getParent(), stream);
+        types = new ArrayList<>(1);
+        types.add(type);
+      }
+      result = new ArrayList<>();
+      for (Type type : types) {
+        if (type != null) {
+
+          Type overallDAType = 
stream.getCas().getDocumentAnnotation().getType();
+          String name = type.getName();
+          if (StringUtils.equals(CAS.TYPE_NAME_DOCUMENT_ANNOTATION, name)
+                  || "org.apache.uima.ruta.type.Document".equals(name)
+                  || overallDAType.equals(type)) {
+            // TODO what about dynamic windowing?
+            result.add(stream.getDocumentAnnotation());
+          } else {
+            result.addAll(stream.getAnnotations(type));
+          }
+          if (expression.getFeatureExpression() != null) {
+            result = new 
ArrayList<>(expression.getFeatureExpression().getAnnotations(result,
+                    CHECK_ON_FEATURE, context, stream));
+          }
+        }
       }
+
     }
     return result;
   }
@@ -96,7 +109,10 @@ public class RutaAnnotationTypeMatcher i
     } else {
       end = lastBasic.getEnd();
     }
-    if (annotation.getEnd() > 0) {
+    if (end == stream.getDocumentAnnotation().getBegin()) {
+      // non existing wildcard match
+      stream.moveToFirst();
+    } else if (annotation.getEnd() > 0) {
       stream.moveTo(lastBasic);
       if (stream.isVisible(lastBasic) && stream.isValid()
               && stream.get().getEnd() == lastBasic.getEnd()) {
@@ -141,24 +157,35 @@ public class RutaAnnotationTypeMatcher i
         }
         return result;
       } else {
-        Type type = getType(parent, stream);
-        Collection<AnnotationFS> anchors = new ArrayList<>();
-        Collection<AnnotationFS> beginAnchors = 
nextBasic.getBeginAnchors(type);
-        if (beginAnchors != null) {
-          for (AnnotationFS afs : beginAnchors) {
-            if (afs.getBegin() >= stream.getDocumentAnnotation().getBegin()
-                    && afs.getEnd() <= 
stream.getDocumentAnnotation().getEnd()) {
-              anchors.add(afs);
+        List<Type> types = null;
+        if (expression.getTypeListExpression() != null) {
+          types = expression.getTypeListExpression().getTypeList(context, 
stream);
+        } else {
+          Type type = getType(context.getParent(), stream);
+          types = new ArrayList<>(1);
+          types.add(type);
+        }
+        List<AnnotationFS> annotations = new ArrayList<>();
+        for (Type type : types) {
+          Collection<AnnotationFS> anchors = new ArrayList<>();
+          Collection<AnnotationFS> beginAnchors = 
nextBasic.getBeginAnchors(type);
+          if (beginAnchors != null) {
+            for (AnnotationFS afs : beginAnchors) {
+              if (afs.getBegin() >= stream.getDocumentAnnotation().getBegin()
+                      && afs.getEnd() <= 
stream.getDocumentAnnotation().getEnd()) {
+                anchors.add(afs);
+              }
             }
           }
-        }
-        if (expression.getFeatureExpression() != null) {
-          return expression.getFeatureExpression().getAnnotations(anchors, 
CHECK_ON_FEATURE,
-                  context, stream);
-        } else {
-          return anchors;
-        }
+          if (expression.getFeatureExpression() != null) {
+            
annotations.addAll(expression.getFeatureExpression().getAnnotations(anchors,
+                    CHECK_ON_FEATURE, context, stream));
+          } else {
+            annotations.addAll(anchors);
+          }
 
+        }
+        return annotations;
       }
 
     }
@@ -179,6 +206,10 @@ public class RutaAnnotationTypeMatcher i
     if (stream.isVisible(firstBasic)) {
       stream.moveToPrevious();
     }
+    if (firstBasic.getBegin() == stream.getDocumentAnnotation().getEnd()) {
+      // non existing wildcard match
+      stream.moveToLast();
+    }
 
     if (stream.isValid()) {
       RutaBasic nextBasic = (RutaBasic) stream.get();
@@ -213,22 +244,33 @@ public class RutaAnnotationTypeMatcher i
           }
         }
       } else {
-        Type type = getType(parent, stream);
-        Collection<AnnotationFS> anchors = new ArrayList<>();
-        Collection<AnnotationFS> endAnchors = nextBasic.getEndAnchors(type);
-        if (endAnchors != null) {
-          for (AnnotationFS afs : endAnchors) {
-            if (afs.getBegin() >= stream.getDocumentAnnotation().getBegin()) {
-              anchors.add(afs);
+        List<Type> types = null;
+        if (expression.getTypeListExpression() != null) {
+          types = expression.getTypeListExpression().getTypeList(context, 
stream);
+        } else {
+          Type type = getType(context.getParent(), stream);
+          types = new ArrayList<>(1);
+          types.add(type);
+        }
+        List<AnnotationFS> annotations = new ArrayList<>();
+        for (Type type : types) {
+          Collection<AnnotationFS> anchors = new ArrayList<>();
+          Collection<AnnotationFS> endAnchors = nextBasic.getEndAnchors(type);
+          if (endAnchors != null) {
+            for (AnnotationFS afs : endAnchors) {
+              if (afs.getBegin() >= stream.getDocumentAnnotation().getBegin()) 
{
+                anchors.add(afs);
+              }
             }
           }
+          if (expression.getFeatureExpression() != null) {
+            
annotations.addAll(expression.getFeatureExpression().getAnnotations(anchors,
+                    CHECK_ON_FEATURE, context, stream));
+          } else {
+            annotations.addAll(anchors);
+          }
         }
-        if (expression.getFeatureExpression() != null) {
-          return expression.getFeatureExpression().getAnnotations(anchors, 
CHECK_ON_FEATURE,
-                  context, stream);
-        } else {
-          return anchors;
-        }
+        return annotations;
       }
     }
     return Collections.emptyList();

Added: 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/rule/RutaOptionalRuleElement.java
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/rule/RutaOptionalRuleElement.java?rev=1849219&view=auto
==============================================================================
--- 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/rule/RutaOptionalRuleElement.java
 (added)
+++ 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/rule/RutaOptionalRuleElement.java
 Tue Dec 18 19:52:18 2018
@@ -0,0 +1,114 @@
+/*
+ * 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.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.uima.cas.text.AnnotationFS;
+import org.apache.uima.ruta.RutaProcessRuntimeException;
+import org.apache.uima.ruta.RutaStream;
+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.visitor.InferenceCrowd;
+
+public class RutaOptionalRuleElement extends RutaRuleElement {
+
+  public RutaOptionalRuleElement(List<AbstractRutaCondition> conditions,
+          List<AbstractRutaAction> actions, RuleElementContainer container, 
RutaBlock parent) {
+
+    super(null, new NormalQuantifier(), conditions, actions, container, 
parent);
+  }
+
+  @Override
+  public Collection<? extends AnnotationFS> getAnchors(RutaStream stream) {
+    throw new RutaProcessRuntimeException(
+            "Using an optional rule lement as anchor is not allowed!");
+  }
+
+  @Override
+  public List<RuleMatch> startMatch(RuleMatch ruleMatch, RuleApply ruleApply,
+          ComposedRuleElementMatch containerMatch, RuleElement entryPoint, 
RutaStream stream,
+          InferenceCrowd crowd) {
+    throw new RutaProcessRuntimeException(
+            "Using an optional rule lement as anchor is not allowed!");
+  }
+
+  @Override
+  protected void doMatch(boolean after, AnnotationFS annotation, RuleMatch 
ruleMatch,
+          ComposedRuleElementMatch containerMatch, boolean ruleAnchor, 
RutaStream stream,
+          InferenceCrowd crowd) {
+    RuleElementMatch result = new RuleElementMatch(this, containerMatch);
+    result.setRuleAnchor(ruleAnchor);
+    List<EvaluatedCondition> evaluatedConditions = new 
ArrayList<EvaluatedCondition>(
+            conditions.size());
+    boolean base = true;
+    MatchContext context = new MatchContext(annotation, this, ruleMatch, 
after);
+
+    List<AnnotationFS> textsMatched = new ArrayList<AnnotationFS>(1);
+    if (annotation != null) {
+      textsMatched.add(annotation);
+    }
+    // already set the matched text and inform others
+    result.setMatchInfo(base, textsMatched, stream);
+    context.getParent().getEnvironment().addMatchToVariable(ruleMatch, this, 
context, stream);
+    if (base) {
+      for (AbstractRutaCondition condition : conditions) {
+        crowd.beginVisit(condition, null);
+        EvaluatedCondition eval = condition.eval(context, stream, crowd);
+        crowd.endVisit(condition, null);
+        evaluatedConditions.add(eval);
+        if (!eval.isValue()) {
+          break;
+        }
+      }
+    }
+    result.setConditionInfo(base, evaluatedConditions);
+    boolean inlinedRulesMatched = matchInnerRules(ruleMatch, stream, crowd);
+    result.setInlinedRulesMatched(inlinedRulesMatched);
+    ruleMatch.setMatched(ruleMatch.matched() && result.matched());
+  }
+
+  @Override
+  protected boolean isNotConsumable(Collection<? extends AnnotationFS> 
nextAnnotations) {
+    return false;
+  }
+
+  @Override
+  public String toString() {
+    return "_";
+  }
+
+  @Override
+  public long estimateAnchors(RutaStream stream) {
+    return Integer.MAX_VALUE;
+  }
+
+  @Override
+  public Collection<? extends AnnotationFS> getNextAnnotations(boolean after,
+          AnnotationFS annotation, RutaStream stream) {
+    return Arrays.asList(stream.getBasicNextTo(!after, annotation));
+  }
+
+}

Modified: 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/rule/RutaRule.java
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/rule/RutaRule.java?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/rule/RutaRule.java 
(original)
+++ 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/rule/RutaRule.java 
Tue Dec 18 19:52:18 2018
@@ -30,6 +30,7 @@ import org.apache.uima.ruta.RutaConstant
 import org.apache.uima.ruta.RutaEnvironment;
 import org.apache.uima.ruta.RutaStatement;
 import org.apache.uima.ruta.RutaStream;
+import org.apache.uima.ruta.action.AbstractRutaAction;
 import org.apache.uima.ruta.block.RutaBlock;
 import org.apache.uima.ruta.visitor.InferenceCrowd;
 
@@ -38,8 +39,8 @@ public class RutaRule extends AbstractRu
   private ComposedRuleElement root;
 
   /**
-   *  labels of all rule elements including those in inlined rules.
-   *  The values store the values of overridden variables.
+   * labels of all rule elements including those in inlined rules. The values 
store the values of
+   * overridden variables.
    */
   private Map<String, Object> labels;
 
@@ -100,6 +101,7 @@ public class RutaRule extends AbstractRu
     if (!StringUtils.isBlank(ruleElement.getLabel())) {
       labels.put(ruleElement.getLabel(), null);
     }
+    fillLabelMapWithActions(ruleElement.getActions());
     if (ruleElement instanceof ComposedRuleElement) {
       ComposedRuleElement cre = (ComposedRuleElement) ruleElement;
       List<RuleElement> ruleElements = cre.getRuleElements();
@@ -111,6 +113,16 @@ public class RutaRule extends AbstractRu
     fillLabelMapWithInlinedRules(ruleElement.getInlinedActionRules());
   }
 
+  private void fillLabelMapWithActions(List<AbstractRutaAction> actions) {
+    if (actions != null) {
+      for (AbstractRutaAction action : actions) {
+        if (action != null && !StringUtils.isBlank(action.getLabel())) {
+          labels.put(action.getLabel(), null);
+        }
+      }
+    }
+  }
+
   private void fillLabelMapWithInlinedRules(List<RutaStatement> rules) {
     if (rules != null) {
       for (RutaStatement eachInlined : rules) {
@@ -124,7 +136,7 @@ public class RutaRule extends AbstractRu
   }
 
   private void prepareEnvironment(MatchContext context, RutaStream stream) {
-    if(isInlined()) {
+    if (isInlined()) {
       // only the actual rule may setup the environment
       return;
     }
@@ -132,15 +144,19 @@ public class RutaRule extends AbstractRu
     RutaEnvironment environment = parent.getEnvironment();
     for (Entry<String, Object> entry : labels.entrySet()) {
       String label = entry.getKey();
-      if(environment.isVariable(label)) {
+      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)) {
+        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 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 (in script " + 
context.getParent().getName() + ")!");
+          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 (in script "
+                  + context.getParent().getName() + ")!");
         }
       } else {
         environment.addVariable(label, 
RutaConstants.RUTA_VARIABLE_ANNOTATION_LIST);
@@ -149,7 +165,7 @@ public class RutaRule extends AbstractRu
   }
 
   private void cleanupEnvironment(MatchContext context, RutaStream stream) {
-    if(isInlined()) {
+    if (isInlined()) {
       // only the actual rule may revert the environment
       return;
     }
@@ -158,15 +174,14 @@ public class RutaRule extends AbstractRu
     for (Entry<String, Object> entry : labels.entrySet()) {
       String label = entry.getKey();
       Object value = entry.getValue();
-      if(value == null) {
+      if (value == null) {
         environment.removeVariable(label);
       } else {
         environment.setVariableValue(label, value);
       }
     }
   }
-  
-  
+
   public ComposedRuleElement getRoot() {
     return root;
   }

Modified: 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/rule/RutaRuleElement.java
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/rule/RutaRuleElement.java?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/rule/RutaRuleElement.java
 (original)
+++ 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/rule/RutaRuleElement.java
 Tue Dec 18 19:52:18 2018
@@ -113,9 +113,9 @@ public class RutaRuleElement extends Abs
       } else {
         if (getContainer() instanceof ComposedRuleElement) {
           ComposedRuleElement composed = (ComposedRuleElement) getContainer();
-          List<RuleMatch> fallbackContinue = composed
-                  .fallbackContinue(true, true, eachAnchor, extendedMatch, 
ruleApply,
-                          extendedContainerMatch, null, entryPoint, stream, 
crowd);
+          List<RuleMatch> fallbackContinue = composed.fallbackContinue(true, 
true, eachAnchor,
+                  extendedMatch, ruleApply, extendedContainerMatch, null, 
entryPoint, stream,
+                  crowd);
           result.addAll(fallbackContinue);
         }
       }
@@ -145,7 +145,8 @@ public class RutaRuleElement extends Abs
                   sideStepOrigin, stream, crowd, entryPoint);
           break;
         }
-        Collection<? extends AnnotationFS> nextAnnotations = 
getNextAnnotations(after, eachAnchor, stream);
+        Collection<? extends AnnotationFS> nextAnnotations = 
getNextAnnotations(after, eachAnchor,
+                stream);
         if (nextAnnotations.size() == 0) {
           stopMatching = true;
           result = stepbackMatch(after, eachAnchor, extendedMatch, ruleApply,
@@ -157,8 +158,8 @@ public class RutaRuleElement extends Abs
           if (this.equals(entryPoint)) {
             result.add(extendedMatch);
           } else if (extendedMatch.matched()) {
-            if (quantifier.continueMatch(after, context, eachAnchor, 
extendedContainerMatch,
-                    stream, crowd)) {
+            if (quantifier.continueMatch(after, context, eachAnchor, 
extendedContainerMatch, stream,
+                    crowd)) {
               // continue in while loop
             } else {
               stopMatching = true;
@@ -183,7 +184,7 @@ public class RutaRuleElement extends Abs
     return result;
   }
 
-  private List<RuleMatch> continueMatchSomewhereElse(boolean after, boolean 
failed,
+  protected List<RuleMatch> continueMatchSomewhereElse(boolean after, boolean 
failed,
           AnnotationFS eachAnchor, RuleMatch extendedMatch, RuleApply 
ruleApply,
           ComposedRuleElementMatch extendedContainerMatch, RutaRuleElement 
sideStepOrigin,
           RuleElement entryPoint, RutaStream stream, InferenceCrowd crowd) {
@@ -209,12 +210,13 @@ public class RutaRuleElement extends Abs
     // if() for really lazy quantifiers
     MatchContext context = new MatchContext(this, ruleMatch, after);
     if (quantifier.continueMatch(after, context, annotation, containerMatch, 
stream, crowd)) {
-      Collection<? extends AnnotationFS> nextAnnotations = 
getNextAnnotations(after, annotation, stream);
-      if (nextAnnotations.isEmpty()) {
+      Collection<? extends AnnotationFS> nextAnnotations = 
getNextAnnotations(after, annotation,
+              stream);
+      if (isNotConsumable(nextAnnotations)) {
         result = stepbackMatch(after, annotation, ruleMatch, ruleApply, 
containerMatch,
                 sideStepOrigin, stream, crowd, entryPoint);
       }
-      boolean useAlternatives = nextAnnotations.size() != 1;
+      boolean useAlternatives = nextAnnotations.size() > 1;
       for (AnnotationFS eachAnchor : nextAnnotations) {
         if (earlyExit(eachAnchor, ruleApply, stream)) {
           // ... for different matching paradigms that avoid some matches
@@ -262,7 +264,11 @@ public class RutaRuleElement extends Abs
     return result;
   }
 
-  private List<RuleMatch> stepbackMatch(boolean after, AnnotationFS annotation,
+  protected boolean isNotConsumable(Collection<? extends AnnotationFS> 
nextAnnotations) {
+    return nextAnnotations.isEmpty();
+  }
+
+  protected List<RuleMatch> stepbackMatch(boolean after, AnnotationFS 
annotation,
           RuleMatch ruleMatch, RuleApply ruleApply, ComposedRuleElementMatch 
containerMatch,
           RutaRuleElement sideStepOrigin, RutaStream stream, InferenceCrowd 
crowd,
           RuleElement entryPoint) {
@@ -280,8 +286,8 @@ public class RutaRuleElement extends Abs
                 containerMatch, sideStepOrigin, entryPoint, stream, crowd);
       } else if (getContainer() instanceof ComposedRuleElement) {
         ComposedRuleElement cre = (ComposedRuleElement) getContainer();
-        result = cre.fallbackContinue(after, true, annotation, ruleMatch, 
ruleApply,
-                containerMatch, sideStepOrigin, entryPoint, stream, crowd);
+        result = cre.fallbackContinue(after, true, annotation, ruleMatch, 
ruleApply, containerMatch,
+                sideStepOrigin, entryPoint, stream, crowd);
         // was:
         // [Peter] why only check the parent? the grandparent could be 
optional!
         // should we add the second part again for the explanation component?
@@ -365,7 +371,7 @@ public class RutaRuleElement extends Abs
     return result;
   }
 
-  private void doMatch(boolean after, AnnotationFS annotation, RuleMatch 
ruleMatch,
+  protected void doMatch(boolean after, AnnotationFS annotation, RuleMatch 
ruleMatch,
           ComposedRuleElementMatch containerMatch, boolean ruleAnchor, 
RutaStream stream,
           InferenceCrowd crowd) {
     RuleElementMatch result = new RuleElementMatch(this, containerMatch);
@@ -389,7 +395,7 @@ public class RutaRuleElement extends Abs
         EvaluatedCondition eval = condition.eval(context, stream, crowd);
         crowd.endVisit(condition, null);
         evaluatedConditions.add(eval);
-        if(!eval.isValue()) {
+        if (!eval.isValue()) {
           break;
         }
       }
@@ -437,8 +443,8 @@ public class RutaRuleElement extends Abs
     return matcher.estimateAnchors(parent, stream);
   }
 
-  public Collection<? extends AnnotationFS> getNextAnnotations(boolean after, 
AnnotationFS annotation,
-          RutaStream stream) {
+  public Collection<? extends AnnotationFS> getNextAnnotations(boolean after,
+          AnnotationFS annotation, RutaStream stream) {
     if (after) {
       return matcher.getAnnotationsAfter(this, annotation, getParent(), 
stream);
     } else {

Modified: 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/verbalize/ScriptVerbalizer.java
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/verbalize/ScriptVerbalizer.java?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/verbalize/ScriptVerbalizer.java
 (original)
+++ 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/verbalize/ScriptVerbalizer.java
 Tue Dec 18 19:52:18 2018
@@ -42,6 +42,7 @@ import org.apache.uima.ruta.rule.Conjunc
 import org.apache.uima.ruta.rule.RegExpRule;
 import org.apache.uima.ruta.rule.RuleElement;
 import org.apache.uima.ruta.rule.RutaMatcher;
+import org.apache.uima.ruta.rule.RutaOptionalRuleElement;
 import org.apache.uima.ruta.rule.RutaRule;
 import org.apache.uima.ruta.rule.RutaRuleElement;
 import org.apache.uima.ruta.rule.WildCardRuleElement;
@@ -149,31 +150,33 @@ public class ScriptVerbalizer {
       result.append(verbalizeConjunct((ConjunctRulesRuleElement) re));
     } else if (re instanceof ComposedRuleElement) {
       result.append(verbalizeComposed((ComposedRuleElement) re));
+    } else if (re instanceof RutaOptionalRuleElement) {
+      result.append("_");
     } else if (re instanceof RutaRuleElement) {
       RutaRuleElement tmre = (RutaRuleElement) re;
       RutaMatcher matcher = tmre.getMatcher();
       // action-only rule
-        IRutaExpression expression = matcher.getExpression();
-        boolean actionOnlyRule = expression == null;
-        if(expression != null) {
-          String verbalize = verbalizer.verbalize(expression);
-          if(StringUtils.isBlank(verbalize)) {
-            actionOnlyRule = true;
-          } else {
-            result.append(verbalize);
-          }
+      IRutaExpression expression = matcher.getExpression();
+      boolean actionOnlyRule = expression == null;
+      if (expression != null) {
+        String verbalize = verbalizer.verbalize(expression);
+        if (StringUtils.isBlank(verbalize)) {
+          actionOnlyRule = true;
+        } else {
+          result.append(verbalize);
         }
-        if (actionOnlyRule) {
-          Iterator<AbstractRutaAction> ait = actions.iterator();
-          while (ait.hasNext()) {
-            AbstractRutaAction each = ait.next();
-            result.append(verbalizer.verbalize(each));
-            if (ait.hasNext()) {
-              result.append(",");
-            }
+      }
+      if (actionOnlyRule) {
+        Iterator<AbstractRutaAction> ait = actions.iterator();
+        while (ait.hasNext()) {
+          AbstractRutaAction each = ait.next();
+          result.append(verbalizer.verbalize(each));
+          if (ait.hasNext()) {
+            result.append(",");
           }
-          return result.toString();
         }
+        return result.toString();
+      }
     } else if (re instanceof WildCardRuleElement) {
       result.append("#");
     }
@@ -262,10 +265,13 @@ public class ScriptVerbalizer {
     return result.toString();
   }
 
-  public String verbalizeMatcher(RutaRuleElement tmre) {
+  public String verbalizeMatcher(RutaRuleElement re) {
+
     StringBuilder result = new StringBuilder();
-    RutaMatcher matcher = tmre.getMatcher();
-    result.append(verbalizer.verbalize(matcher.getExpression()));
+    RutaMatcher matcher = re.getMatcher();
+    if (matcher != null) {
+      result.append(verbalizer.verbalize(matcher.getExpression()));
+    }
     return result.toString();
   }
 
@@ -337,8 +343,7 @@ public class ScriptVerbalizer {
           sb.append("(");
           Iterator<Entry<IStringExpression, IRutaExpression>> fit = 
map.entrySet().iterator();
           while (fit.hasNext()) {
-            Map.Entry<IStringExpression, IRutaExpression> entry = fit
-                    .next();
+            Map.Entry<IStringExpression, IRutaExpression> entry = fit.next();
             sb.append(verbalizer.verbalize(entry.getKey()));
             sb.append(" = ");
             sb.append(verbalizer.verbalize(entry.getValue()));

Modified: 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/visitor/DebugInfoFactory.java
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/visitor/DebugInfoFactory.java?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/visitor/DebugInfoFactory.java
 (original)
+++ 
uima/ruta/trunk/ruta-core/src/main/java/org/apache/uima/ruta/visitor/DebugInfoFactory.java
 Tue Dec 18 19:52:18 2018
@@ -71,8 +71,8 @@ public class DebugInfoFactory {
           boolean addToIndex, boolean withMatches, Map<RutaElement, Long> 
timeInfo) {
     JCas cas = stream.getJCas();
     DebugBlockApply dba = new DebugBlockApply(cas);
-    AnnotationFS matchedAnnotation = ruleMatch.getMatchedAnnotationsOfElement(
-            ruleMatch.getRule().getRoot()).get(0);
+    AnnotationFS matchedAnnotation = ruleMatch
+            
.getMatchedAnnotationsOfElement(ruleMatch.getRule().getRoot()).get(0);
     dba.setElement(matchedAnnotation.getCoveredText());
     dba.setBegin(matchedAnnotation.getBegin());
     dba.setEnd(matchedAnnotation.getEnd());
@@ -127,8 +127,8 @@ public class DebugInfoFactory {
       }
       dba.setInnerApply(UIMAUtils.toFSArray(cas, innerApply));
       dba.setElement(verbalize);
-      DebugRuleApply ruleApply = 
createDebugRuleApply(blockApply.getRuleApply(), stream,
-              addToIndex, withMatches, timeInfo);
+      DebugRuleApply ruleApply = 
createDebugRuleApply(blockApply.getRuleApply(), stream, addToIndex,
+              withMatches, timeInfo);
       dba.setApplied(ruleApply.getApplied());
       dba.setTried(ruleApply.getTried());
       dba.setRules(ruleApply.getRules());
@@ -148,8 +148,8 @@ public class DebugInfoFactory {
       }
       dba.setInnerApply(UIMAUtils.toFSArray(cas, innerApply));
       dba.setElement(verbalize);
-      DebugRuleApply ruleApply = 
createDebugRuleApply(blockApply.getRuleApply(), stream,
-              addToIndex, withMatches, timeInfo);
+      DebugRuleApply ruleApply = 
createDebugRuleApply(blockApply.getRuleApply(), stream, addToIndex,
+              withMatches, timeInfo);
       dba.setApplied(ruleApply.getApplied());
       dba.setTried(ruleApply.getTried());
       dba.setRules(ruleApply.getRules());
@@ -176,8 +176,8 @@ public class DebugInfoFactory {
     int end = 0;
     if (withMatches) {
       for (AbstractRuleMatch<? extends AbstractRule> match : 
ruleApply.getList()) {
-        DebugRuleMatch debugRuleMatch = createDebugRuleMatch(match, stream, 
addToIndex,
-                withMatches, timeInfo);
+        DebugRuleMatch debugRuleMatch = createDebugRuleMatch(match, stream, 
addToIndex, withMatches,
+                timeInfo);
         begin = Math.min(begin, debugRuleMatch.getBegin());
         end = Math.max(end, debugRuleMatch.getEnd());
         ruleMatches.add(debugRuleMatch);

Modified: 
uima/ruta/trunk/ruta-core/src/test/java/org/apache/uima/ruta/action/CreateTest.java
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-core/src/test/java/org/apache/uima/ruta/action/CreateTest.java?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- 
uima/ruta/trunk/ruta-core/src/test/java/org/apache/uima/ruta/action/CreateTest.java
 (original)
+++ 
uima/ruta/trunk/ruta-core/src/test/java/org/apache/uima/ruta/action/CreateTest.java
 Tue Dec 18 19:52:18 2018
@@ -22,11 +22,14 @@ package org.apache.uima.ruta.action;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
+import java.io.IOException;
+import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
 
+import org.apache.uima.analysis_engine.AnalysisEngineProcessException;
 import org.apache.uima.cas.CAS;
 import org.apache.uima.cas.FSIterator;
 import org.apache.uima.cas.Feature;
@@ -34,16 +37,21 @@ import org.apache.uima.cas.FeatureStruct
 import org.apache.uima.cas.Type;
 import org.apache.uima.cas.text.AnnotationFS;
 import org.apache.uima.cas.text.AnnotationIndex;
+import org.apache.uima.resource.ResourceConfigurationException;
+import org.apache.uima.resource.ResourceInitializationException;
 import org.apache.uima.ruta.engine.Ruta;
 import org.apache.uima.ruta.engine.RutaEngine;
 import org.apache.uima.ruta.engine.RutaTestUtils;
 import org.apache.uima.ruta.engine.RutaTestUtils.TestFeature;
+import org.apache.uima.util.InvalidXMLException;
 import org.junit.Test;
 
 public class CreateTest {
 
   @Test
-  public void test() {
+  public void test() throws AnalysisEngineProcessException, 
InvalidXMLException,
+          ResourceInitializationException, ResourceConfigurationException, 
URISyntaxException,
+          IOException {
     String name = this.getClass().getSimpleName();
     String namespace = 
this.getClass().getPackage().getName().replaceAll("\\.", "/");
 
@@ -61,15 +69,9 @@ public class CreateTest {
     String fn3 = "count";
     list.add(new TestFeature(fn3, "", "uima.cas.Integer"));
 
-    CAS cas = null;
-    try {
-      cas = RutaTestUtils.process(namespace + "/" + name + 
RutaEngine.SCRIPT_FILE_EXTENSION,
-              namespace + "/" + name + ".txt", 50, false, false, complexTypes, 
features, namespace
-                      + "/");
-    } catch (Exception e) {
-      e.printStackTrace();
-      assert (false);
-    }
+    CAS cas = RutaTestUtils.process(namespace + "/" + name + 
RutaEngine.SCRIPT_FILE_EXTENSION,
+            namespace + "/" + name + ".txt", 50, false, false, complexTypes, 
features,
+            namespace + "/");
     Type t = null;
     AnnotationIndex<AnnotationFS> ai = null;
     FSIterator<AnnotationFS> iterator = null;

Added: 
uima/ruta/trunk/ruta-core/src/test/java/org/apache/uima/ruta/action/LabelAtActionTest.java
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-core/src/test/java/org/apache/uima/ruta/action/LabelAtActionTest.java?rev=1849219&view=auto
==============================================================================
--- 
uima/ruta/trunk/ruta-core/src/test/java/org/apache/uima/ruta/action/LabelAtActionTest.java
 (added)
+++ 
uima/ruta/trunk/ruta-core/src/test/java/org/apache/uima/ruta/action/LabelAtActionTest.java
 Tue Dec 18 19:52:18 2018
@@ -0,0 +1,57 @@
+/*
+ * 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.action;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+import org.apache.uima.analysis_engine.AnalysisEngineProcessException;
+import org.apache.uima.cas.CAS;
+import org.apache.uima.resource.ResourceConfigurationException;
+import org.apache.uima.resource.ResourceInitializationException;
+import org.apache.uima.ruta.engine.Ruta;
+import org.apache.uima.ruta.engine.RutaTestUtils;
+import org.apache.uima.util.InvalidXMLException;
+import org.junit.Test;
+
+public class LabelAtActionTest {
+
+  @Test
+  public void test() throws ResourceInitializationException, 
InvalidXMLException, IOException,
+          AnalysisEngineProcessException, ResourceConfigurationException, 
URISyntaxException {
+
+    String document = "This is a test.";
+    String script = "CW{-> t:T1}->{t{->T2};};";
+    script += "SW.ct == \"is\"{-> t:MARK(T3)}->{t{->T4};};";
+    script += "SW.ct == \"a\"{-> t:CREATE(T5)}->{t{->T6};};";
+    script += "SW.ct == \"test\"{-> t:GATHER(T7)}->{t{->T8};};";
+    script += "Document{-> t:MARKFIRST(T9)}->{t{->T10};};";
+    script += "Document{-> t:MARKLAST(T11)}->{t{->T12};};";
+
+    CAS cas = RutaTestUtils.getCAS(document);
+    Ruta.apply(cas, script);
+
+    RutaTestUtils.assertAnnotationsEquals(cas, 2, 1, "This");
+    RutaTestUtils.assertAnnotationsEquals(cas, 4, 1, "is");
+    RutaTestUtils.assertAnnotationsEquals(cas, 6, 1, "a");
+    RutaTestUtils.assertAnnotationsEquals(cas, 8, 1, "test");
+
+  }
+
+}

Modified: 
uima/ruta/trunk/ruta-core/src/test/java/org/apache/uima/ruta/engine/RutaEngineTest.java
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-core/src/test/java/org/apache/uima/ruta/engine/RutaEngineTest.java?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- 
uima/ruta/trunk/ruta-core/src/test/java/org/apache/uima/ruta/engine/RutaEngineTest.java
 (original)
+++ 
uima/ruta/trunk/ruta-core/src/test/java/org/apache/uima/ruta/engine/RutaEngineTest.java
 Tue Dec 18 19:52:18 2018
@@ -53,8 +53,8 @@ public class RutaEngineTest {
     AnalysisEngine ae = AnalysisEngineFactory.createEngine(RutaEngine.class,
             RutaEngine.PARAM_VAR_NAMES, new String[] { "typeVar" }, 
RutaEngine.PARAM_VAR_VALUES,
             new String[] { "TruePositive" }, RutaEngine.PARAM_RULES, script,
-            RutaEngine.PARAM_INDEX_ONLY_MENTIONED_TYPES, 
Boolean.valueOf(true), RutaEngine.PARAM_INDEX_ADDITONALLY,
-            new String[] { "COMMA" });
+            RutaEngine.PARAM_INDEX_ONLY_MENTIONED_TYPES, Boolean.valueOf(true),
+            RutaEngine.PARAM_INDEX_ADDITONALLY, new String[] { "COMMA" });
     RutaEngine engine = (RutaEngine) FieldUtils.readField(ae, 
"mAnalysisComponent", true);
 
     TypeUsageInformation typeUsageInformation = (TypeUsageInformation) 
FieldUtils.readField(engine,
@@ -85,19 +85,19 @@ public class RutaEngineTest {
             "org.apache.uima.ruta.type.WS", "uima.tcas.DocumentAnnotation"), 
usedTypesList);
 
   }
-  
+
   @Test
-  public void testInitializeVariableValues() throws 
ResourceInitializationException, InvalidXMLException, IOException, 
AnalysisEngineProcessException{
-    
+  public void testInitializeVariableValues() throws 
ResourceInitializationException,
+          InvalidXMLException, IOException, AnalysisEngineProcessException {
+
     String document = "Some text.";
     String script = "BOOLEAN var4 = false;";
-    script +="(CW SW) {var4 -> T1};";
-    
-    AnalysisEngine ae = AnalysisEngineFactory.createEngine(RutaEngine.class,
-            RutaEngine.PARAM_RULES, script,
-            RutaEngine.PARAM_VAR_NAMES, new String[] {"var1", "var2", "var3", 
"var4"},
-            RutaEngine.PARAM_VAR_VALUES, new String[] {"false", "false", 
"false", "true"});
-    
+    script += "(CW SW) {var4 -> T1};";
+
+    AnalysisEngine ae = AnalysisEngineFactory.createEngine(RutaEngine.class, 
RutaEngine.PARAM_RULES,
+            script, RutaEngine.PARAM_VAR_NAMES, new String[] { "var1", "var2", 
"var3", "var4" },
+            RutaEngine.PARAM_VAR_VALUES, new String[] { "false", "false", 
"false", "true" });
+
     CAS cas = RutaTestUtils.getCAS(document);
     ae.process(cas);
     RutaTestUtils.assertAnnotationsEquals(cas, 1, 1);

Modified: 
uima/ruta/trunk/ruta-core/src/test/java/org/apache/uima/ruta/expression/AnnotationTypeExpressionTest.java
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-core/src/test/java/org/apache/uima/ruta/expression/AnnotationTypeExpressionTest.java?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- 
uima/ruta/trunk/ruta-core/src/test/java/org/apache/uima/ruta/expression/AnnotationTypeExpressionTest.java
 (original)
+++ 
uima/ruta/trunk/ruta-core/src/test/java/org/apache/uima/ruta/expression/AnnotationTypeExpressionTest.java
 Tue Dec 18 19:52:18 2018
@@ -38,7 +38,7 @@ public class AnnotationTypeExpressionTes
     String script = "";
     script += "Document{-> CREATE(Struct1, \"as\" = COMMA)};\n";
     script += "Struct1.as{-> T1};\n";
-    
+
     Map<String, String> complexTypes = new TreeMap<String, String>();
     complexTypes.put("Struct1", "uima.tcas.Annotation");
     complexTypes.put("Struct2", "uima.tcas.Annotation");
@@ -48,11 +48,27 @@ public class AnnotationTypeExpressionTes
     features.put("Struct2", list);
     list.add(new TestFeature("a", "", "uima.tcas.Annotation"));
     list.add(new TestFeature("as", "", "uima.cas.FSArray"));
-    
+
     CAS cas = RutaTestUtils.getCAS(document, complexTypes, features);
-    Ruta.apply(cas, script);    
-    
+    Ruta.apply(cas, script);
+
     RutaTestUtils.assertAnnotationsEquals(cas, 1, 0);
   }
 
+  @Test
+  public void testTypeList() throws Exception {
+    String document = "This is a test.";
+    String script = "TYPELIST tl = {CW,PERIOD};\n";
+    script += "tl{-> T1};\n";
+    script += "SW tl{-> T2};\n";
+    script += "tl{-> T3} @SW;\n";
+
+    CAS cas = RutaTestUtils.getCAS(document);
+    Ruta.apply(cas, script);
+
+    RutaTestUtils.assertAnnotationsEquals(cas, 1, 2, "This", ".");
+    RutaTestUtils.assertAnnotationsEquals(cas, 2, 1, ".");
+    RutaTestUtils.assertAnnotationsEquals(cas, 3, 1, "This");
+  }
+
 }

Added: 
uima/ruta/trunk/ruta-core/src/test/java/org/apache/uima/ruta/rule/RutaOptionalRuleElementTest.java
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-core/src/test/java/org/apache/uima/ruta/rule/RutaOptionalRuleElementTest.java?rev=1849219&view=auto
==============================================================================
--- 
uima/ruta/trunk/ruta-core/src/test/java/org/apache/uima/ruta/rule/RutaOptionalRuleElementTest.java
 (added)
+++ 
uima/ruta/trunk/ruta-core/src/test/java/org/apache/uima/ruta/rule/RutaOptionalRuleElementTest.java
 Tue Dec 18 19:52:18 2018
@@ -0,0 +1,42 @@
+/*
+ * 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 org.apache.uima.cas.CAS;
+import org.apache.uima.ruta.engine.Ruta;
+import org.apache.uima.ruta.engine.RutaTestUtils;
+import org.junit.Test;
+
+public class RutaOptionalRuleElementTest {
+
+  @Test
+  public void test() throws Exception {
+    String document = "This is a Test";
+    String script = "_{-PARTOF(CW)} @W{-> T1};\n";
+    script += "@W{-> T2} _{-PARTOF(CW)};\n";
+
+    CAS cas = RutaTestUtils.getCAS(document);
+    Ruta.apply(cas, script);
+
+    RutaTestUtils.assertAnnotationsEquals(cas, 1, 3, "This", "a", "Test");
+    RutaTestUtils.assertAnnotationsEquals(cas, 2, 3, "This", "is", "Test");
+
+  }
+
+}

Modified: 
uima/ruta/trunk/ruta-core/src/test/java/org/apache/uima/ruta/rule/SidestepInComposedTest.java
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-core/src/test/java/org/apache/uima/ruta/rule/SidestepInComposedTest.java?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- 
uima/ruta/trunk/ruta-core/src/test/java/org/apache/uima/ruta/rule/SidestepInComposedTest.java
 (original)
+++ 
uima/ruta/trunk/ruta-core/src/test/java/org/apache/uima/ruta/rule/SidestepInComposedTest.java
 Tue Dec 18 19:52:18 2018
@@ -19,28 +19,32 @@
 
 package org.apache.uima.ruta.rule;
 
+import java.io.IOException;
+import java.net.URISyntaxException;
+
+import org.apache.uima.analysis_engine.AnalysisEngineProcessException;
 import org.apache.uima.cas.CAS;
+import org.apache.uima.resource.ResourceConfigurationException;
+import org.apache.uima.resource.ResourceInitializationException;
 import org.apache.uima.ruta.engine.Ruta;
 import org.apache.uima.ruta.engine.RutaTestUtils;
+import org.apache.uima.util.InvalidXMLException;
 import org.junit.Test;
 
 public class SidestepInComposedTest {
 
   @Test
-  public void test() {
+  public void test() throws ResourceInitializationException, 
InvalidXMLException, IOException,
+          AnalysisEngineProcessException, ResourceConfigurationException, 
URISyntaxException {
     String document = "15. Mai 2005";
     String script = "\"Mai\" -> T1;";
     script += "NUM{->T2} PERIOD @T1 NUM;\n";
-    CAS cas = null;
-    try {
-      cas = RutaTestUtils.getCAS(document);
-      Ruta.apply(cas, script);
-    } catch (Exception e) {
-      e.printStackTrace();
-    }
-    
+
+    CAS cas = RutaTestUtils.getCAS(document);
+    Ruta.apply(cas, script);
+
     RutaTestUtils.assertAnnotationsEquals(cas, 2, 1, "15");
-      
+
     cas.release();
   }
 }

Modified: 
uima/ruta/trunk/ruta-docbook/src/docbook/tools.ruta.language.syntax.xml
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-docbook/src/docbook/tools.ruta.language.syntax.xml?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- uima/ruta/trunk/ruta-docbook/src/docbook/tools.ruta.language.syntax.xml 
(original)
+++ uima/ruta/trunk/ruta-docbook/src/docbook/tools.ruta.language.syntax.xml Tue 
Dec 18 19:52:18 2018
@@ -125,10 +125,12 @@ RuleElements           -> RuleElement+
 RuleElement            -> (Identifier ":")? "@"? 
                         RuleElementType | RuleElementLiteral
                         | RuleElementComposed | RuleElementWildCard
+                        | RuleElementOptional
 RuleElementType        ->  AnnotationTypeExpr OptionalRuleElementPart
 RuleElementWithCA      ->  AnnotationTypeExpr ("{" Conditions?  
                            Actions? "}")?
-AnnotationTypeExpr     -> (TypeExpression | AnnotationExpression) 
+AnnotationTypeExpr     -> (TypeExpression | AnnotationExpression
+                                                  TypeListExpression | 
AnnotationListExpression) 
                           (Operator)? Expression ("{" Conditions "}")?
 FeatureMatchExpression -> TypeExpression ( "." Feature)+ 
                           ( Operator (Expression | "null"))?
@@ -142,11 +144,13 @@ OptionalRuleElementPart-> QuantifierPart
 InlinedRules           ->  ("<-" "{" SimpleStatement+ "}")?
                            ("->"  "{" SimpleStatement+ "}")?
 RuleElementWildCard    -> "#"("{" Conditions?  Actions? }")? InlinedRules?
+RuleElementOptional    -> "_"("{" Conditions?  Actions? }")? InlinedRules?
 QuantifierPart         -> "*" | "*?" | "+" | "+?" | "?" | "??"
                         | "[" NumberExpression "," NumberExpression "]"
                         | "[" NumberExpression "," NumberExpression "]?"
 Conditions             -> Condition ( "," Condition )*
-Actions                -> "->" Action ( "," Action)*
+Actions                -> "->" (Identifier ":")? Action 
+                         ( "," (Identifier ":")? Action)*
 ]]></programlisting>
     Since each condition and each action has its own syntax, conditions
     and actions are described in their own section. For conditions see

Modified: uima/ruta/trunk/ruta-docbook/src/docbook/tools.ruta.language.xml
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-docbook/src/docbook/tools.ruta.language.xml?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- uima/ruta/trunk/ruta-docbook/src/docbook/tools.ruta.language.xml (original)
+++ uima/ruta/trunk/ruta-docbook/src/docbook/tools.ruta.language.xml Tue Dec 18 
19:52:18 2018
@@ -147,7 +147,7 @@ Dr.JoachimBaumeister
       
       <programlisting><![CDATA[PERIOD #{-> Sentence} 
PERIOD;]]></programlisting>
       
-      In this example, everything in beteen two periods is annotated with an 
annotation of the type
+      In this example, everything in between two periods is annotated with an 
annotation of the type
       <code>Sentence</code>. This rule is much more efficient than a rule like 
       <code>PERIOD ANY+{-PARTOF(PERIOD)} PERIOD;</code> since it only 
navigated in the index of PERIOD annotations 
       and does not match on all tokens.
@@ -159,12 +159,12 @@ Dr.JoachimBaumeister
       
       This rule creates only annotations after a period. If the wildcard is 
used as an anchor of the rule, 
       e.g., is the first rule element and no manual anchor is specified, then 
it starts to match at the beginning 
-      of the doucment or current window.
+      of the document or current window.
       
       <programlisting><![CDATA[(# PERIOD){-> Sentence};]]></programlisting>
       
       This rule creates a Sentence annotation starting at the begin of the 
document ending with the first period.
-      If the rule lements are swicthed, the result is quite different because 
of the starting anchor of the rule:
+      If the rule elements are switched, the result is quite different because 
of the starting anchor of the rule:
       
       <programlisting><![CDATA[(PERIOD #){-> Sentence};]]></programlisting>
       
@@ -175,6 +175,21 @@ Dr.JoachimBaumeister
     </para>
   </section>
   
+  <section id="ugr.tools.ruta.language.optional">
+    <title>Optional match _</title>
+    <para>
+      The optional match <code>_</code> is a special matching condition of a 
rule element, 
+      which does not require any annotations or a textual span in general to 
match.
+      The functionality of the optional match is illustrated with following 
examples:
+      
+      <programlisting><![CDATA[PERIOD{-> SentenceEnd} 
_{-PARTOF(CW)};]]></programlisting>
+      
+      In this example, an annotation of the type <code>SentenceEnd</code> is 
created for each <code>PERIOD</code> annotation, 
+      if it is followed by something that is not part of a <code>CW</code>. 
This is also fulfilled for the last <code>PERIOD</code> annotation
+      in a document that ends with a period.
+    </para>
+  </section>
+  
   <section id="ugr.tools.ruta.language.labels">
     <title>Label expressions</title>
     <para>
@@ -182,13 +197,13 @@ Dr.JoachimBaumeister
       multiple annotations - the annotations matched by the matching condition 
of the rule element. 
       The name of the variable is the short identifier before the colon in 
front of the matching condition, e.g., 
       in <code>sw:SW</code>, <code>SW</code> is the matching condition and 
<code>sw</code> is the name of the local variable.
-      The variable will be assigned when the rule element tries to match (also 
when it fails afterall) 
-      and can be utilzed in all other language elements afterwards.
+      The variable will be assigned when the rule element tries to match (also 
when it fails after all) 
+      and can be utilized in all other language elements afterwards.
       The functionality of the label expressions is illustrated with following 
examples:
       
       <programlisting><![CDATA[sw1:SW 
sw2:SW{sw1.end=sw2.begin};]]></programlisting>
       
-      This rule matches on two consecutive small-written words, but matches 
only if there is no space inbetween them.
+      This rule matches on two consecutive small-written words, but matches 
only if there is no space in between them.
       
       Label expression can also be used across <xref 
linkend='ugr.tools.ruta.language.inlined' />.
     </para>

Modified: 
uima/ruta/trunk/ruta-ep-ide-ui/src/main/java/org/apache/uima/ruta/ide/validator/RuleElementLabelVisitor.java
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-ep-ide-ui/src/main/java/org/apache/uima/ruta/ide/validator/RuleElementLabelVisitor.java?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- 
uima/ruta/trunk/ruta-ep-ide-ui/src/main/java/org/apache/uima/ruta/ide/validator/RuleElementLabelVisitor.java
 (original)
+++ 
uima/ruta/trunk/ruta-ep-ide-ui/src/main/java/org/apache/uima/ruta/ide/validator/RuleElementLabelVisitor.java
 Tue Dec 18 19:52:18 2018
@@ -23,22 +23,29 @@ import java.util.Collection;
 import java.util.HashSet;
 
 import org.apache.commons.lang3.StringUtils;
+import org.apache.uima.ruta.ide.parser.ast.RutaAction;
 import org.apache.uima.ruta.ide.parser.ast.RutaRuleElement;
 import org.eclipse.dltk.ast.ASTVisitor;
 import org.eclipse.dltk.ast.expressions.Expression;
 
-public class RuleElementLabelVisitor extends ASTVisitor{
+public class RuleElementLabelVisitor extends ASTVisitor {
 
   private Collection<String> labels = new HashSet<>();
-  
+
   @Override
   public boolean visit(Expression s) throws Exception {
-    if(s instanceof RutaRuleElement) {
+    if (s instanceof RutaRuleElement) {
       RutaRuleElement element = (RutaRuleElement) s;
-      if(!StringUtils.isBlank(element.getLabel())) {
+      if (!StringUtils.isBlank(element.getLabel())) {
         labels.add(element.getLabel());
       }
       return true;
+    } else if (s instanceof RutaAction) {
+      RutaAction action = (RutaAction) s;
+      if (!StringUtils.isBlank(action.getLabel())) {
+        labels.add(action.getLabel());
+      }
+      return true;
     }
     return false;
   }

Propchange: 
uima/ruta/trunk/ruta-ep-ide/src/main/antlr3/org/apache/uima/ruta/ide/core/parser/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Tue Dec 18 19:52:18 2018
@@ -0,0 +1 @@
+output

Modified: 
uima/ruta/trunk/ruta-ep-ide/src/main/antlr3/org/apache/uima/ruta/ide/core/parser/RutaLexer.g
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-ep-ide/src/main/antlr3/org/apache/uima/ruta/ide/core/parser/RutaLexer.g?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- 
uima/ruta/trunk/ruta-ep-ide/src/main/antlr3/org/apache/uima/ruta/ide/core/parser/RutaLexer.g
 (original)
+++ 
uima/ruta/trunk/ruta-ep-ide/src/main/antlr3/org/apache/uima/ruta/ide/core/parser/RutaLexer.g
 Tue Dec 18 19:52:18 2018
@@ -388,6 +388,9 @@ ADDRESS_PREFIX
 
 STARTANCHOR 
   :  '@';
+  
+       
+OPTIONAL       : '_' ;
 
 HexLiteral : '0' ('x'|'X') HexDigit+ IntegerTypeSuffix? ;
 

Modified: 
uima/ruta/trunk/ruta-ep-ide/src/main/antlr3/org/apache/uima/ruta/ide/core/parser/RutaParser.g
URL: 
http://svn.apache.org/viewvc/uima/ruta/trunk/ruta-ep-ide/src/main/antlr3/org/apache/uima/ruta/ide/core/parser/RutaParser.g?rev=1849219&r1=1849218&r2=1849219&view=diff
==============================================================================
--- 
uima/ruta/trunk/ruta-ep-ide/src/main/antlr3/org/apache/uima/ruta/ide/core/parser/RutaParser.g
 (original)
+++ 
uima/ruta/trunk/ruta-ep-ide/src/main/antlr3/org/apache/uima/ruta/ide/core/parser/RutaParser.g
 Tue Dec 18 19:52:18 2018
@@ -816,7 +816,7 @@ String label = null;
        re1 = ruleElementType {re = re1;}
        | re2 = ruleElementLiteral {re = re2;}
        | re3 = ruleElementComposed {re = re3;}
-       | re4 = ruleElementWildCard {re = re4;}
+       | re4 = ruleElementSpecial {re = re4;}
        )
        {re.setLabel(label);}
        (t = THEN2 LCURLY (rule = simpleStatement {innerRules.add(rule);})+ 
@@ -825,12 +825,12 @@ String label = null;
        RCURLY {re.setInlinedRules(innerRules);re.setInlineMode(t == null ? 
null : t.getText());})?
        ;
 
-ruleElementWildCard returns [RutaRuleElement re = null] 
+ruleElementSpecial returns [RutaRuleElement re = null] 
 @init{
 List<RutaCondition> dummyConds = new ArrayList<RutaCondition>();
 }
   :
-       w = WILDCARD
+       w = (WILDCARD | OPTIONAL)
         (LCURLY 
         {
         
@@ -849,6 +849,7 @@ List<RutaCondition> dummyConds = new Arr
 
     ;
 
+
        
 ruleElementComposed returns [ComposedRuleElement re = null] 
 @init{
@@ -1548,8 +1549,10 @@ result = ActionFactory.createEmptyAction
 action returns [RutaAction result = null]
 @init {
 result = ActionFactory.createEmptyAction(input.LT(1));
+String label = null;
 }
        :
+       (l = Identifier {label = l.getText();} COLON)?
        (
        a = actionColor
        | a = actionDel
@@ -1600,7 +1603,7 @@ result = ActionFactory.createEmptyAction
        | (typeExpression)=> te = typeExpression {a = 
ActionFactory.createAction(te);}
        
        // | a = variableAction
-       ) {result = a;}
+       ) {result = a; result.setLabel(label);}
        ;
 
 


Reply via email to