bugfixes mainly...
authorzapata <zapata>
Tue, 20 Aug 2002 02:15:33 +0000 (02:15 +0000)
committerzapata <zapata>
Tue, 20 Aug 2002 02:15:33 +0000 (02:15 +0000)
source/mir/producer/EntityBatchingProducerNode.java
source/mir/producer/GeneratingProducerNode.java
source/mir/producer/ResourceBundleProducerNode.java
source/mir/producer/reader/DefaultProducerNodeBuilders.java
source/mir/producer/reader/ProducerConfigReader.java
source/mir/producer/reader/ScriptedProducerNodeDefinition.java
source/mir/util/ParameterExpander.java

index ce256ff..e12bd79 100755 (executable)
@@ -16,9 +16,10 @@ public class EntityBatchingProducerNode implements ProducerNode {
   private String definition;
   private String whereClause;
   private String orderByClause;
-  private int nrEntitiesToSkip;
-  private int nrEntitiesPerBatch;
-  private int minNrEntitiesInFirstBatch;
+  private String nrEntitiesToSkipExpression;
+  private String nrEntitiesPerBatchExpression;
+  private String minNrEntitiesInFirstBatchExpression;
+  private String defaultNrBatchesToProcessExpression;
   private ProducerNode batchSubNode;
   private ProducerNode batchListSubNode;
 
@@ -29,16 +30,15 @@ public class EntityBatchingProducerNode implements ProducerNode {
         String aDefinition,
         String aWhereClause,
         String anOrderByClause,
-        int aNrEntitiesPerBatch,
-        int aMinNrEntitiesInFirstBatch,
-        int aNrEntitiesToSkip,
+        int anrEntitiesPerBatchExpression,
+        int aminNrEntitiesInFirstBatchExpression,
+        int anrEntitiesToSkipExpression,
         ProducerNode aBatchSubNode) {
     this(aBatchDataKey, aBatchInfoKey, aModel, aDefinition, aWhereClause,
-        anOrderByClause, aNrEntitiesPerBatch, aMinNrEntitiesInFirstBatch, aNrEntitiesToSkip,
+        anOrderByClause, anrEntitiesPerBatchExpression, aminNrEntitiesInFirstBatchExpression, anrEntitiesToSkipExpression,
         aBatchSubNode, null);
   }
 
-
   public EntityBatchingProducerNode(
         String aBatchDataKey,
         String aBatchInfoKey,
@@ -52,6 +52,30 @@ public class EntityBatchingProducerNode implements ProducerNode {
         ProducerNode aBatchSubNode,
         ProducerNode aBatchListSubNode) {
 
+    this(aBatchDataKey, aBatchInfoKey, aModel, aDefinition,
+         aWhereClause, anOrderByClause,
+        Integer.toString(aNrEntitiesPerBatch),
+        Integer.toString(aMinNrEntitiesInFirstBatch),
+        Integer.toString(aNrEntitiesToSkip),
+        "1",
+        aBatchSubNode, aBatchListSubNode);
+
+  }
+
+  public EntityBatchingProducerNode(
+        String aBatchDataKey,
+        String aBatchInfoKey,
+        EntityAdapterModel aModel,
+        String aDefinition,
+        String aWhereClause,
+        String anOrderByClause,
+        String anrEntitiesPerBatchExpression,
+        String aminNrEntitiesInFirstBatchExpression,
+        String anrEntitiesToSkipExpression,
+        String aDefaultNrBatchesToProcessExpression,
+        ProducerNode aBatchSubNode,
+        ProducerNode aBatchListSubNode) {
+
     batchSubNode = aBatchSubNode;
     batchListSubNode = aBatchListSubNode;
 
@@ -63,9 +87,10 @@ public class EntityBatchingProducerNode implements ProducerNode {
     definition = aDefinition;
     whereClause = aWhereClause;
     orderByClause = anOrderByClause;
-    nrEntitiesToSkip = aNrEntitiesToSkip;
-    nrEntitiesPerBatch = aNrEntitiesPerBatch;
-    minNrEntitiesInFirstBatch = aMinNrEntitiesInFirstBatch;
+    nrEntitiesToSkipExpression = anrEntitiesToSkipExpression;
+    nrEntitiesPerBatchExpression = anrEntitiesPerBatchExpression;
+    minNrEntitiesInFirstBatchExpression = aminNrEntitiesInFirstBatchExpression;
+    defaultNrBatchesToProcessExpression = aDefaultNrBatchesToProcessExpression;
   }
 
   public void produce(Map aValueMap, String aVerb, PrintWriter aLogger) throws ProducerFailure {
@@ -85,17 +110,29 @@ public class EntityBatchingProducerNode implements ProducerNode {
     List batchLocations;
     BatchLocation location;
 
+    int nrEntitiesToSkip;
+    int nrEntitiesPerBatch;
+    int minNrEntitiesInFirstBatch;
+
 //  ML: The order by clause should lead to a result set in _reverse order_: the first row will be
 //      the last entity presented on the last page
 
 
-    if (verb==null)
-      throw new ProducerFailure("EntityBatchingProducerNode: unknown verb '"+aVerb+"'", null);
-
     try {
+      if (verb==null) {
+        nrBatchesToProcess = ParameterExpander.evaluateIntegerExpressionWithDefault( aValueMap, defaultNrBatchesToProcessExpression, -1 );
+      }
+      else {
+        nrBatchesToProcess=verb.nrBatchesToProcess;
+      }
+
       expandedWhereClause = ParameterExpander.expandExpression( aValueMap, whereClause );
       expandedOrderByClause = ParameterExpander.expandExpression( aValueMap, orderByClause );
 
+      nrEntitiesToSkip = ParameterExpander.evaluateIntegerExpression( aValueMap, nrEntitiesToSkipExpression);
+      nrEntitiesPerBatch = ParameterExpander.evaluateIntegerExpression( aValueMap, nrEntitiesPerBatchExpression);
+      minNrEntitiesInFirstBatch = ParameterExpander.evaluateIntegerExpression( aValueMap, minNrEntitiesInFirstBatchExpression);
+
       batchesData = new Vector();
       batchLocations = new Vector();
 
@@ -108,16 +145,16 @@ public class EntityBatchingProducerNode implements ProducerNode {
       batchLocations.add(new BatchLocation(nrBatchesAfterFirst*nrEntitiesPerBatch, nrEntitiesInFirstBatch));
       batchData = new HashMap();
       batchData.put("identifier", "");
-      batchData.put("index", Integer.toString(nrBatchesAfterFirst+1));
-      batchData.put("size", Integer.toString(nrEntitiesInFirstBatch));
+      batchData.put("index", new Integer(nrBatchesAfterFirst+1));
+      batchData.put("size", new Integer(nrEntitiesInFirstBatch));
       batchesData.add(batchData);
 
       for (i=0; i<nrBatchesAfterFirst; i++) {
         batchLocations.add(1, new BatchLocation(i*nrEntitiesPerBatch, nrEntitiesPerBatch));
         batchData = new HashMap();
-        batchData.put("identifier", Integer.toString(i));
-        batchData.put("index", Integer.toString(i+1));
-        batchData.put("size", Integer.toString(nrEntitiesPerBatch));
+        batchData.put("identifier", new Integer(i));
+        batchData.put("index", new Integer(i+1));
+        batchData.put("size", new Integer(nrEntitiesPerBatch));
         batchesData.add(1, batchData);
       }
 
@@ -126,15 +163,14 @@ public class EntityBatchingProducerNode implements ProducerNode {
       batchData.put("all", batchesData);
       batchData.put("first", batchesData.get(0));
       batchData.put("last", batchesData.get(batchesData.size()-1));
-      batchData.put("count", Integer.toString(batchesData.size()));
+      batchData.put("count", new Integer(batchesData.size()));
 
       if (batchListSubNode!=null) {
         batchListSubNode.produce(aValueMap, aVerb, aLogger);
       }
 
-      nrBatchesToProcess = nrBatchesAfterFirst+1;
-      if (verb.nrBatchesToProcess>-1 && verb.nrBatchesToProcess<nrBatchesToProcess) {
-        nrBatchesToProcess=verb.nrBatchesToProcess;
+      if (nrBatchesToProcess<0 || nrBatchesToProcess>nrBatchesAfterFirst+1) {
+        nrBatchesToProcess = nrBatchesAfterFirst+1;
       }
 
       if (batchSubNode!=null) {
index 07bece9..e37abe0 100755 (executable)
@@ -56,7 +56,7 @@ public class GeneratingProducerNode implements ProducerNode {
     }
     endTime = System.currentTimeMillis();
 
-    aLogger.println("  Time: " + (endTime-startTime) + " ms<br>");
+    aLogger.println("  Time: " + (endTime-startTime) + " ms");
     aLogger.flush();
   }
 
index 730d62a..e172d11 100755 (executable)
@@ -26,12 +26,13 @@ public class ResourceBundleProducerNode extends ProducerNodeDecorator {
   public void produce(Map aValueMap, String aVerb, PrintWriter aLogger) throws ProducerFailure {
     Object messages;
 
+
     try {
       if (languageIdentifier!=null) {
         messages =
             new ResourceBundleGeneratorFunction(
-                new Locale(ParameterExpander.expandExpression( aValueMap, languageIdentifier ), null ),
-                MessageResources.getMessageResources(ParameterExpander.expandExpression( aValueMap, bundleIdentifier ))
+                new Locale(ParameterExpander.expandExpression( aValueMap, languageIdentifier ), "" ),
+                MessageResources.getMessageResources( ParameterExpander.expandExpression( aValueMap, bundleIdentifier ))
             );
       }
       else {
index aa145cc..97feb4f 100755 (executable)
@@ -156,8 +156,8 @@ public class DefaultProducerNodeBuilders {
 
       key = (String) anAttributes.get(ENUMERATION_KEY_ATTRIBUTE);
       definition = (String) anAttributes.get(ENUMERATION_DEFINITION_ATTRIBUTE);
-      selection = (String) anAttributes.get(ENUMERATION_SELECTION_ATTRIBUTE);
-      order = (String) anAttributes.get(ENUMERATION_ORDER_ATTRIBUTE);
+      selection = (String) ReaderTool.getStringAttributeWithDefault(anAttributes, ENUMERATION_SELECTION_ATTRIBUTE, "");
+      order = (String) ReaderTool.getStringAttributeWithDefault(anAttributes, ENUMERATION_ORDER_ATTRIBUTE, "");
       limit = (String) anAttributes.get(ENUMERATION_LIMIT_ATTRIBUTE);
       skip = (String) anAttributes.get(ENUMERATION_SKIP_ATTRIBUTE);
     };
@@ -191,7 +191,7 @@ public class DefaultProducerNodeBuilders {
     private final static String   LIST_SKIP_ATTRIBUTE = SKIP_ATTRIBUTE;
     private final static String[] LIST_REQUIRED_ATTRIBUTES = { LIST_KEY_ATTRIBUTE, LIST_DEFINITION_ATTRIBUTE };
     private final static String[] LIST_OPTIONAL_ATTRIBUTES = { LIST_SELECTION_ATTRIBUTE, LIST_ORDER_ATTRIBUTE, LIST_SKIP_ATTRIBUTE, LIST_LIMIT_ATTRIBUTE};
-    private final static String[] LIST_SUBNODES = {LIST_DEFAULT_SUBNODE};
+    private final static String[] LIST_SUBNODES = {};
 
     private String key;
     private String definition;
@@ -212,14 +212,14 @@ public class DefaultProducerNodeBuilders {
 
       key = (String) anAttributes.get(LIST_KEY_ATTRIBUTE);
       definition = (String) anAttributes.get(LIST_DEFINITION_ATTRIBUTE);
-      selection = (String) anAttributes.get(LIST_SELECTION_ATTRIBUTE);
-      order = (String) anAttributes.get(LIST_ORDER_ATTRIBUTE);
+      selection = (String) ReaderTool.getStringAttributeWithDefault(anAttributes, LIST_SELECTION_ATTRIBUTE, "");
+      order = (String) ReaderTool.getStringAttributeWithDefault(anAttributes, LIST_ORDER_ATTRIBUTE, "");
       limit = (String) anAttributes.get(LIST_LIMIT_ATTRIBUTE);
       skip = (String) anAttributes.get(LIST_SKIP_ATTRIBUTE);
     };
 
     public ProducerNode constructNode() {
-      return new EntityListProducerNode(key, model, definition, selection, order, limit, skip, getSubNode(LIST_DEFAULT_SUBNODE ));
+      return new EntityListProducerNode(key, model, definition, selection, order, limit, skip, null );
     };
 
     public static class factory implements ProducerNodeBuilderFactory {
@@ -269,7 +269,7 @@ public class DefaultProducerNodeBuilders {
     private final static String   RESOURCEBUNDLE_DEFAULT_SUBNODE = "default";
     private final static String[] RESOURCEBUNDLE_REQUIRED_ATTRIBUTES = { RESOURCEBUNDLE_KEY_ATTRIBUTE, RESOURCEBUNDLE_BUNDLE_ATTRIBUTE };
     private final static String[] RESOURCEBUNDLE_OPTIONAL_ATTRIBUTES = { RESOURCEBUNDLE_LANGUAGE_ATTRIBUTE};
-    private final static String[] RESOURCEBUNDLE_SUBNODES = {RESOURCEBUNDLE_DEFAULT_SUBNODE};
+    private final static String[] RESOURCEBUNDLE_SUBNODES = {};
 
     private String key;
     private String bundle;
@@ -288,7 +288,7 @@ public class DefaultProducerNodeBuilders {
     };
 
     public ProducerNode constructNode() {
-      return new ResourceBundleProducerNode(key, bundle, language, getSubNode(RESOURCEBUNDLE_DEFAULT_SUBNODE ));
+      return new ResourceBundleProducerNode(key, bundle, language, null );
     };
   }
 
@@ -434,10 +434,12 @@ public class DefaultProducerNodeBuilders {
     private final static String   BATCHER_MINBATCHSIZE_ATTRIBUTE = "minbatchsize";
     private final static String   BATCHER_SKIP_ATTRIBUTE = SKIP_ATTRIBUTE;
 
+    private final static String   BATCHER_PROCESS_ATTRIBUTE = "process";
+
     private final static String   BATCHER_BATCH_SUBNODE = "batches";
     private final static String   BATCHER_BATCHLIST_SUBNODE = "batchlist";
     private final static String[] BATCHER_REQUIRED_ATTRIBUTES = { BATCHER_DATAKEY_ATTRIBUTE, BATCHER_INFOKEY_ATTRIBUTE, BATCHER_DEFINITION_ATTRIBUTE, BATCHER_BATCHSIZE_ATTRIBUTE };
-    private final static String[] BATCHER_OPTIONAL_ATTRIBUTES = { BATCHER_SELECTION_ATTRIBUTE, BATCHER_ORDER_ATTRIBUTE, BATCHER_MINBATCHSIZE_ATTRIBUTE, BATCHER_SKIP_ATTRIBUTE };
+    private final static String[] BATCHER_OPTIONAL_ATTRIBUTES = { BATCHER_SELECTION_ATTRIBUTE, BATCHER_ORDER_ATTRIBUTE, BATCHER_MINBATCHSIZE_ATTRIBUTE, BATCHER_SKIP_ATTRIBUTE, BATCHER_PROCESS_ATTRIBUTE };
     private final static String[] BATCHER_SUBNODES = { BATCHER_BATCH_SUBNODE, BATCHER_BATCHLIST_SUBNODE };
 
     // ML: batchSize, minBatchSize, skip should be expressions!
@@ -448,10 +450,10 @@ public class DefaultProducerNodeBuilders {
     private String definition;
     private String selection;
     private String order;
-    private int batchSize;
-    private int minBatchSize;
-    private int skip;
-
+    private String batchSize;
+    private String minBatchSize;
+    private String skip;
+    private String process;
 
     public BatchingProducerNodeBuilder(EntityAdapterModel aModel) {
       super(BATCHER_SUBNODES);
@@ -468,9 +470,10 @@ public class DefaultProducerNodeBuilders {
       selection = ReaderTool.getStringAttributeWithDefault(anAttributes, BATCHER_SELECTION_ATTRIBUTE, "" );
       order = ReaderTool.getStringAttributeWithDefault(anAttributes, BATCHER_ORDER_ATTRIBUTE, "" );
 
-      batchSize = ReaderTool.getIntegerAttributeWithDefault(anAttributes, BATCHER_BATCHSIZE_ATTRIBUTE, 20 );
-      minBatchSize = ReaderTool.getIntegerAttributeWithDefault(anAttributes, BATCHER_MINBATCHSIZE_ATTRIBUTE, 0 );
-      skip = ReaderTool.getIntegerAttributeWithDefault(anAttributes, BATCHER_SKIP_ATTRIBUTE, 0 );
+      batchSize = ReaderTool.getStringAttributeWithDefault(anAttributes, BATCHER_BATCHSIZE_ATTRIBUTE, "20" );
+      minBatchSize = ReaderTool.getStringAttributeWithDefault(anAttributes, BATCHER_MINBATCHSIZE_ATTRIBUTE, "0" );
+      skip = ReaderTool.getStringAttributeWithDefault(anAttributes, BATCHER_SKIP_ATTRIBUTE, "0" );
+      process = ReaderTool.getStringAttributeWithDefault(anAttributes, BATCHER_PROCESS_ATTRIBUTE, "-1" );
     };
 
     public ProducerNode constructNode() {
@@ -484,6 +487,7 @@ public class DefaultProducerNodeBuilders {
           batchSize,
           minBatchSize,
           skip,
+          process,
           getSubNode( BATCHER_BATCH_SUBNODE ),
           getSubNode( BATCHER_BATCHLIST_SUBNODE )
       );
@@ -537,6 +541,34 @@ public class DefaultProducerNodeBuilders {
 
 ////////////////////////////////////////////////////////////////////////////////
 
+  public static class ScriptedProducerParameterNodeBuilder implements ProducerNodeBuilder {
+    private String parameterName;
+    private String scriptedNodeName;
+
+    public ScriptedProducerParameterNodeBuilder(String aScriptedNodeName, String aParameterName) {
+      parameterName = aParameterName;
+      scriptedNodeName = aScriptedNodeName;
+    }
+
+    public void setSubNode(String aName, ProducerNode aNode) {
+    };
+
+    public Set getAvailableSubNodes() {
+      return new HashSet();
+    };
+
+    public void setAttributes(Map anAttributes) throws ProducerConfigExc {
+      if (!anAttributes.isEmpty())
+        throw new ProducerConfigExc("No parameters allowed here");
+    };
+
+    public ProducerNode constructNode() {
+      return new ScriptedProducerNodeDefinition.NodeParameterProducerNode(scriptedNodeName, parameterName);
+    };
+  }
+
+////////////////////////////////////////////////////////////////////////////////
+
   public static class ScriptedProducerNodeBuilder implements ProducerNodeBuilder {
     private ScriptedProducerNodeDefinition definition;
     private Map nodeParameterValues;
index f673250..1b78791 100755 (executable)
@@ -280,8 +280,8 @@ public class ProducerConfigReader {
         name = (String) anAttributes.get(NODE_DEFINITION_NAME_ATTRIBUTE);
         ReaderTool.checkValidIdentifier( name );
 
-        if (producers.containsKey(name))
-          throw new ProducerConfigExc("Duplicate producer name: '" + name + "'");
+//        if (producers.containsKey(name))
+//          throw new ProducerConfigExc("Duplicate producer name: '" + name + "'");
 
         name = (String) anAttributes.get(NODE_DEFINITION_NAME_ATTRIBUTE);
 
@@ -296,7 +296,6 @@ public class ProducerConfigReader {
         producers.put(name, ((ProducerSectionHandler) aHandler).getProducerFactory());
       }
       else if (aHandler instanceof NodeDefinitionSectionHandler) {
-
         scriptedNodeBuilderLibrary.registerFactory(name,
             new DefaultProducerNodeBuilders.ScriptedProducerNodeBuilder.factory(
                 ((NodeDefinitionSectionHandler) aHandler).getDefinition()));
@@ -488,8 +487,10 @@ public class ProducerConfigReader {
         if (!anAttributes.isEmpty()) {
           throw new ProducerConfigExc( "No attributes allowed" );
         }
-        producerNode.addSubNode(new ScriptedProducerNodeDefinition.NodeParameterProducerNode(scriptedNodeName, aTag));
 
+        currentBuilder = new DefaultProducerNodeBuilders.ScriptedProducerParameterNodeBuilder(scriptedNodeName, aTag);
+//        producerNode.addSubNode(
+//        new ScriptedProducerNodeDefinition.NodeParameterProducerNode(scriptedNodeName, aTag));
         return new EmptySectionHandler();
       }
       else if (scriptedNodeBuilderLibrary.hasBuilderForName(aTag) || builderLibrary.hasBuilderForName((aTag))) {
@@ -555,13 +556,13 @@ public class ProducerConfigReader {
 
   public class NodeDefinitionSectionHandler extends SectionHandler {
     private ScriptedProducerNodeDefinition nodeDefinition;
-    private ProducerNode definition;
+    private ProducerNode body;
     private Map stringParameters;
     private Map nodeParameters;
     private String name;
 
     public NodeDefinitionSectionHandler(String aName) {
-      definition = null;
+      body = null;
       nodeParameters = null;
       stringParameters = null;
       name = aName;
@@ -575,7 +576,7 @@ public class ProducerConfigReader {
         if (nodeParameters!=null) {
           throw new ProducerConfigExc( "Parameters have already been declared" );
         }
-        if (definition!=null) {
+        if (body!=null) {
           throw new ProducerConfigExc( "Parameters should come before definition" );
         }
 
@@ -584,7 +585,7 @@ public class ProducerConfigReader {
       else if (aTag.equals("definition")) {
         return new ProducerNodeSectionHandler(name, nodeParameters.keySet());
       }
-      else throw new ProducerConfigExc("Only 'verb' tags allowed here, '" + aTag + "' encountered.");
+      else throw new ProducerConfigExc("Only 'definition' or 'parameters' tags allowed here, '" + aTag + "' encountered.");
     }
 
     public void endElement(SectionHandler aHandler) {
@@ -593,17 +594,19 @@ public class ProducerConfigReader {
         nodeParameters = ((NodeDefinitionParametersSectionHandler) aHandler).getNodeParameters();
       }
       else if (aHandler instanceof ProducerNodeSectionHandler) {
-        definition = ((ProducerNodeSectionHandler) aHandler).getProducerNode();
+        body = ((ProducerNodeSectionHandler) aHandler).getProducerNode();
       }
     }
 
     public void finishSection() throws ProducerConfigExc {
       Iterator i;
-      if (definition == null)
+      if (body == null)
         throw new ProducerConfigExc( "Definition missing" );
 
       nodeDefinition = new ScriptedProducerNodeDefinition(name);
 
+      nodeDefinition.setBody(body);
+
       i = nodeParameters.keySet().iterator();
       while (i.hasNext()) {
         nodeDefinition.addNodeParameter((String) i.next());
index b9d9ec2..e12b8d5 100755 (executable)
@@ -63,7 +63,7 @@ public class ScriptedProducerNodeDefinition {
     while (i.hasNext()) {
       Map.Entry entry = (Map.Entry) i.next();
 
-      if (entry.getValue() == null && aRequired ) {
+      if ((entry.getValue() == null) == aRequired ) {
         result.add(entry.getKey());
       }
     }
index a695cb3..8db5736 100755 (executable)
@@ -129,8 +129,6 @@ public class ParameterExpander {
           }
           if (endOfExpressionPosition<anExpression.length()) {
             result.append(evaluateStringExpression(aMap, anExpression.substring(position+2, endOfExpressionPosition)));
-//            findStringForKey(aMap, anExpression.substring(position+2, endOfExpressionPosition))
-//            );
             previousPosition=endOfExpressionPosition+1;
           }
           else {
@@ -210,8 +208,8 @@ public class ParameterExpander {
       return result;
     }
 
-    public int getPosition() {
-      return position;
+    public String getPositionString() {
+      return data.substring(0, position) + "<__>" + data.substring(position) ;
     }
   }
 
@@ -221,8 +219,6 @@ public class ParameterExpander {
   public static abstract class PunctuationToken extends Token { public PunctuationToken() { }; }
     private static class LeftSquareBraceToken extends PunctuationToken {};
     private static class RightSquareBraceToken extends PunctuationToken {};
-    private static class ANDToken extends PunctuationToken {};
-    private static class ORToken extends PunctuationToken {};
     private static class EqualsToken extends PunctuationToken {};
     private static class EqualsNotToken extends PunctuationToken {};
     private static class NOTToken extends PunctuationToken {};
@@ -269,9 +265,12 @@ public class ParameterExpander {
   private static class Scanner {
     private Reader reader;
     private Token nextToken;
+    private String positionString;
 
     public Scanner(Reader aReader) {
       reader = aReader;
+      skipWhitespace();
+      positionString = reader.getPositionString();
     }
 
     public Token scanStringLiteral() {
@@ -299,8 +298,8 @@ public class ParameterExpander {
       return new LiteralToken(result.toString());
     }
 
-    public int getPosition() {
-      return reader.getPosition();
+    public String getPositionString() {
+      return positionString;
     }
 
     private Token scanNumber() {
@@ -338,23 +337,6 @@ public class ParameterExpander {
       switch(c.charValue()) {
         case '[': return new LeftSquareBraceToken();
         case ']': return new RightSquareBraceToken();
-        case '&':
-          if (reader.hasNext() && reader.peek().charValue() == '&') {
-            reader.getNext();
-            return new ANDToken();
-          }
-          else {
-            return new ConcatenateToken();
-          }
-        case '|':
-          if (reader.hasNext() && reader.peek().charValue() == '|') {
-            reader.getNext();
-            return new ORToken();
-          }
-          else {
-            throw new RuntimeException("Unknown character: '|'");
-          }
-
         case '=':
           if (reader.hasNext() && reader.peek().charValue() == '=') {
             reader.getNext();
@@ -378,7 +360,14 @@ public class ParameterExpander {
         case ')': return new RightParenthesisToken ();
         case ',': return new CommaToken ();
         case '.': return new PeriodToken ();
-        case '+': return new PlusToken ();
+        case '+':
+          if (reader.hasNext() && reader.peek().charValue() == '+') {
+            reader.getNext();
+            return new ConcatenateToken();
+          }
+          else {
+            return new PlusToken ();
+          }
         case '*': return new TimesToken ();
         case '/': return new DivideToken ();
         case '-': return new MinusToken ();
@@ -426,47 +415,49 @@ public class ParameterExpander {
     }
 
     public Token scanNext() {
-      skipWhitespace();
+      Token result = null;
 
-      if (!reader.hasNext())
-        return null;
+      skipWhitespace();
 
-      Character c = reader.peek();
+      if (reader.hasNext()) {
+        Character c = reader.peek();
 
-      switch(c.charValue()) {
-        case '\'':
-        case '"': return scanStringLiteral();
+        switch(c.charValue()) {
+          case '\'':
+          case '"':
+            result = scanStringLiteral();
+            break;
 
-        default: {
-          if (isIdentifierStart(c.charValue())) {
-            return scanIdentifierKeyword();
-          }
-          else if (isNumberStart(c.charValue())) {
-            return scanNumber();
+          default: {
+            if (isIdentifierStart(c.charValue())) {
+              result = scanIdentifierKeyword();
+            }
+            else if (isNumberStart(c.charValue())) {
+              result = scanNumber();
+            }
+            else
+              result = scanPunctuation();
           }
-          else
-            return scanPunctuation();
         }
       }
-    }
 
-    public Token scan() {
-      Token result = null;
+      skipWhitespace();
 
-      if (nextToken!=null) {
-        result = nextToken;
-      }
-      else {
-        result = scanNext();
-      }
+      return result;
+    }
 
-      nextToken=null;
+    public Token scan() {
+      Token result = peek();
+      nextToken = null;
+      positionString = reader.getPositionString();
 
       return result;
     }
 
     public Token peek() {
-      nextToken = scan();
+      if (nextToken==null) {
+        nextToken = scanNext();
+      }
 
       return nextToken;
     }
@@ -490,8 +481,7 @@ public class ParameterExpander {
         return interpretAsBoolean(parseWhole());
       }
       catch (Throwable t) {
-        t.printStackTrace(System.out);
-        throw new RuntimeException("Parser error at position "+getLocation()+":"+t.getMessage());
+        throw new RuntimeException("Parser error at '" + getLocation()+ "': "+t.getMessage());
       }
     }
 
@@ -500,8 +490,7 @@ public class ParameterExpander {
         return interpretAsInteger(parseWhole());
       }
       catch (Throwable t) {
-        t.printStackTrace(System.out);
-        throw new RuntimeException("Parser error at position "+getLocation()+":"+t.getMessage());
+        throw new RuntimeException("Parser error at '" + getLocation()+ "': "+t.getMessage());
       }
     }
 
@@ -510,14 +499,12 @@ public class ParameterExpander {
         return interpretAsString(parseWhole());
       }
       catch (Throwable t) {
-        throw new RuntimeException("Parser error at position "+getLocation()+":"+t.getMessage());
+        throw new RuntimeException("Parser error at '" + getLocation()+ "': "+t.getMessage());
       }
     }
 
     private String getLocation() {
-      int position = scanner.getPosition();
-
-      return Integer.toString(position);
+      return scanner.getPositionString();
     }
 
     private Object parseWhole() {
@@ -559,7 +546,8 @@ public class ParameterExpander {
         result.add(expression);
 
         token = scanner.scan();
-      } while (token instanceof CommaToken);
+      }
+      while (token instanceof CommaToken);
 
       if (!(token instanceof RightParenthesisToken)) {
         throw new RuntimeException(") or , expected");
@@ -683,8 +671,8 @@ public class ParameterExpander {
     }
 
     private int binaryOperatorLevel(Token aToken) {
-      if ((aToken instanceof ANDToken) ||
-          (aToken instanceof ORToken))
+      if (isANDOperator(aToken) ||
+          isOROperator(aToken))
         return LOGICAL_OPERATOR_LEVEL;
 
       if ((aToken instanceof EqualsToken) ||
@@ -712,10 +700,16 @@ public class ParameterExpander {
       return (aToken instanceof IdentifierToken && ((IdentifierToken) aToken).getName().equals("in"));
     }
 
+    private boolean isANDOperator(Token aToken) {
+      return (aToken instanceof IdentifierToken && ((IdentifierToken) aToken).getName().equals("and"));
+    }
+
+    private boolean isOROperator(Token aToken) {
+      return (aToken instanceof IdentifierToken && ((IdentifierToken) aToken).getName().equals("or"));
+    }
+
     private boolean isBinaryOperator(Token aToken) {
       return
-           (aToken instanceof ANDToken) ||
-           (aToken instanceof ORToken) ||
            (aToken instanceof EqualsToken) ||
            (aToken instanceof EqualsNotToken) ||
            (aToken instanceof PlusToken) ||
@@ -727,7 +721,9 @@ public class ParameterExpander {
            (aToken instanceof LessThanToken) ||
            (aToken instanceof GreaterThanOrEqualsToken) ||
            (aToken instanceof GreaterThanToken) ||
-           isINOperator(aToken);
+           isINOperator(aToken) ||
+           isOROperator(aToken) ||
+           isANDOperator(aToken);
     }
 
     private boolean interpretAsBoolean(Object aValue) {
@@ -738,8 +734,6 @@ public class ParameterExpander {
     }
 
     private int interpretAsInteger(Object aValue) {
-      System.out.println("  interpret: " + aValue );
-
       if (aValue instanceof Integer)
         return ((Integer) aValue).intValue();
 
@@ -795,9 +789,9 @@ public class ParameterExpander {
         return Boolean.FALSE;
       }
 
-      if (aToken instanceof ANDToken)
+      if (isANDOperator(aToken))
         return new Boolean(interpretAsBoolean(aValue1) && interpretAsBoolean(aValue2));
-      if (aToken instanceof ORToken)
+      if (isOROperator(aToken))
         return new Boolean(interpretAsBoolean(aValue1) || interpretAsBoolean(aValue2));
       if (aToken instanceof EqualsToken) {
         return new Boolean(areEqual(aValue1, aValue2));
@@ -814,7 +808,7 @@ public class ParameterExpander {
         return new Integer(interpretAsInteger(aValue1) - interpretAsInteger(aValue2));
 
       if (aToken instanceof ConcatenateToken)
-        return interpretAsString(aValue1) + interpretAsString(aValue1);
+        return interpretAsString(aValue1) + interpretAsString(aValue2);
 
       if (aToken instanceof LessThanOrEqualsToken)
         return new Boolean(interpretAsInteger(aValue1) <= interpretAsInteger(aValue2));