support for CAPTCHAs
[mir.git] / source / mir / util / ParameterExpander.java
index 1378728..0dbd4b3 100755 (executable)
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  * In addition, as a special exception, The Mir-coders gives permission to link
- * the code of this program with  any library licensed under the Apache Software License, 
- * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library 
- * (or with modified versions of the above that use the same license as the above), 
- * and distribute linked combinations including the two.  You must obey the 
- * GNU General Public License in all respects for all of the code used other than 
- * the above mentioned libraries.  If you modify this file, you may extend this 
- * exception to your version of the file, but you are not obligated to do so.  
+ * the code of this program with  any library licensed under the Apache Software License,
+ * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library
+ * (or with modified versions of the above that use the same license as the above),
+ * and distribute linked combinations including the two.  You must obey the
+ * GNU General Public License in all respects for all of the code used other than
+ * the above mentioned libraries.  If you modify this file, you may extend this
+ * exception to your version of the file, but you are not obligated to do so.
  * If you do not wish to do so, delete this exception statement from your version.
  */
 package mir.util;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Vector;
 
 import mir.generator.Generator;
 import mir.generator.GeneratorExc;
 import multex.Exc;
 
+import org.apache.commons.beanutils.MethodUtils;
+import org.apache.commons.beanutils.PropertyUtils;
+
 public class ParameterExpander {
   final static String NODE_SEPARATOR = ".";
   final static char STRING_ESCAPE_CHARACTER = '\\';
 
+  /**
+   * Fundamental method to retrieve a field of an object. Supported are
+   *  maps, beans and objects with a generic get method  
+   */
+  public static Object getObjectField(Object anObject, Object aField) {
+    if (anObject instanceof Map) {
+      return ((Map) anObject).get(aField);
+    }
+    else if ((aField instanceof String) && PropertyUtils.isReadable(anObject, (String) aField)) {
+      try {
+        return PropertyUtils.getProperty(anObject, (String) aField);
+      }
+      catch (Throwable t) {
+        throw new RuntimeException(t.getMessage());
+      }
+    }
+    else {
+      try {
+        return MethodUtils.invokeExactMethod(anObject, "get", aField);
+      }
+      catch (Throwable t) {
+        throw new RuntimeException("Invalid reference of " + aField + " into " + anObject);
+      }
+    }
+  }
+
   private static Object findNode(String aKey, Map aMap, List aParts, boolean aMakeIfNotPresent) throws Exception {
     Iterator i;
     String location = "";
@@ -71,7 +100,7 @@ public class ParameterExpander {
           ((Map) node).put(part, newNode);
         }
         else
-          throw new ParameterExpanderExc( "Can't expand key {1}: {2} does not exist", new Object[]{aKey,location} );
+          throw new ParameterExpanderExc( "Can't expand key " + aKey + ": " + location + " does not exist");
 
       node = newNode;
     }
@@ -92,7 +121,7 @@ public class ParameterExpander {
     Object expandedValue = findValueForKey(aMap, aKey);
 
     if (!(expandedValue instanceof String))
-      throw new ParameterExpanderExc( "Value of key is not a string but a {1}", new Object[]{expandedValue.getClass().getName()} );
+      throw new ParameterExpanderExc( "Value of key is not a string but a " + expandedValue.getClass().getName());
 
     return (String) expandedValue;
   }
@@ -105,14 +134,15 @@ public class ParameterExpander {
 
     Object node=findNode(aKey, aMap, parts, true);
 
+    // todo: bean support
     if (node instanceof Map) {
       ((Map) node).put(key, aValue);
     }
     else
-      throw new ParameterExpanderExc( "Can't set key {1}: not inside a Map", new Object[]{aKey} );
+      throw new ParameterExpanderExc( "Can't set key " + aKey + " : not inside a Map");
   }
 
-  public static String expandExpression(Map aMap, String anExpression) throws Exception {
+  public static String expandExpression(Object aContext, String anExpression) throws Exception {
     int previousPosition = 0;
     int position;
     int endOfExpressionPosition;
@@ -140,17 +170,17 @@ public class ParameterExpander {
                 endOfExpressionPosition++;
               }
               if (endOfExpressionPosition>=anExpression.length()) {
-                throw new ParameterExpanderExc("Unterminated string in {1}",new Object[]{anExpression});
+                throw new ParameterExpanderExc("Unterminated string in '" +anExpression+"'");
               }
             }
             endOfExpressionPosition++;
           }
           if (endOfExpressionPosition<anExpression.length()) {
-            result.append(evaluateStringExpression(aMap, anExpression.substring(position+2, endOfExpressionPosition)));
+            result.append(evaluateStringExpression(aContext, anExpression.substring(position+2, endOfExpressionPosition)));
             previousPosition=endOfExpressionPosition+1;
           }
           else {
-            throw new ParameterExpanderExc("Missing } in {1}",new Object[]{anExpression});
+            throw new ParameterExpanderExc("Missing } in " + anExpression);
           }
         }
         else
@@ -165,33 +195,33 @@ public class ParameterExpander {
     return result.toString();
   }
 
-  public static boolean evaluateBooleanExpression(Map aMap, String anExpression) throws Exception {
+  public static boolean evaluateBooleanExpression(Object aMap, String anExpression) throws Exception {
     Parser parser = new Parser(anExpression, aMap);
 
     return parser.parseBoolean();
   }
 
-  public static String evaluateStringExpression(Map aMap, String anExpression) throws Exception {
+  public static String evaluateStringExpression(Object aMap, String anExpression) throws Exception {
     Parser parser = new Parser(anExpression, aMap);
 
     return parser.parseString();
   }
 
-  public static int evaluateIntegerExpressionWithDefault(Map aMap, String anExpression, int aDefault) throws Exception {
+  public static int evaluateIntegerExpressionWithDefault(Object aMap, String anExpression, int aDefault) throws Exception {
     if (anExpression == null || anExpression.trim().equals(""))
       return aDefault;
     else
       return evaluateIntegerExpression(aMap, anExpression);
   }
 
-  public static int evaluateIntegerExpression(Map aMap, String anExpression) throws Exception {
+  public static int evaluateIntegerExpression(Object aMap, String anExpression) throws Exception {
     Parser parser = new Parser(anExpression, aMap);
 
     return parser.parseInteger();
   }
 
-  public static Object evaluateExpression(Map aMap, String anExpression) throws Exception {
-    Parser parser = new Parser(anExpression, aMap);
+  public static Object evaluateExpression(Object aRoot, String anExpression) throws Exception {
+    Parser parser = new Parser(anExpression, aRoot);
 
     return parser.parseWhole();
   }
@@ -234,25 +264,25 @@ public class ParameterExpander {
   private static abstract class Token {
   }
 
-  public static abstract class PunctuationToken extends Token { public PunctuationToken() { }; }
-    private static class LeftSquareBraceToken extends PunctuationToken {};
-    private static class RightSquareBraceToken extends PunctuationToken {};
-    private static class EqualsToken extends PunctuationToken {};
-    private static class EqualsNotToken extends PunctuationToken {};
-    private static class NOTToken extends PunctuationToken {};
-    private static class LeftParenthesisToken extends PunctuationToken {};
-    private static class RightParenthesisToken extends PunctuationToken {};
-    private static class CommaToken extends PunctuationToken {};
-    private static class PeriodToken extends PunctuationToken {};
-    private static class PlusToken extends PunctuationToken {};
-    private static class TimesToken extends PunctuationToken {};
-    private static class DivideToken extends PunctuationToken {};
-    private static class MinusToken extends PunctuationToken {};
-    private static class ConcatenateToken extends PunctuationToken {};
-    private static class LessThanOrEqualsToken extends PunctuationToken {};
-    private static class GreaterThanOrEqualsToken extends PunctuationToken {};
-    private static class LessThanToken extends PunctuationToken {};
-    private static class GreaterThanToken extends PunctuationToken {};
+  public static abstract class PunctuationToken extends Token { public PunctuationToken() { } }
+    private static class LeftSquareBraceToken extends PunctuationToken {}
+    private static class RightSquareBraceToken extends PunctuationToken {}
+    private static class EqualsToken extends PunctuationToken {}
+    private static class EqualsNotToken extends PunctuationToken {}
+    private static class NOTToken extends PunctuationToken {}
+    private static class LeftParenthesisToken extends PunctuationToken {}
+    private static class RightParenthesisToken extends PunctuationToken {}
+    private static class CommaToken extends PunctuationToken {}
+    private static class PeriodToken extends PunctuationToken {}
+    private static class PlusToken extends PunctuationToken {}
+    private static class TimesToken extends PunctuationToken {}
+    private static class DivideToken extends PunctuationToken {}
+    private static class MinusToken extends PunctuationToken {}
+    private static class ConcatenateToken extends PunctuationToken {}
+    private static class LessThanOrEqualsToken extends PunctuationToken {}
+    private static class GreaterThanOrEqualsToken extends PunctuationToken {}
+    private static class LessThanToken extends PunctuationToken {}
+    private static class GreaterThanToken extends PunctuationToken {}
 
 
   private static class IdentifierToken extends Token {
@@ -414,7 +444,7 @@ public class ParameterExpander {
     public void skipWhitespace() {
       while (reader.hasNext() && Character.isWhitespace(reader.peek().charValue()))
         reader.getNext();
-    };
+    }
 
     private boolean isIdentifierStart(char c) {
       return Character.isLetter(c) || (c == '_');
@@ -487,9 +517,9 @@ public class ParameterExpander {
 
   private static class Parser {
     private Scanner scanner;
-    private Map valueMap;
+    private Object valueMap;
 
-    public Parser(String anExpression, Map aValueMap) {
+    public Parser(String anExpression, Object aValueMap) {
       scanner = new Scanner(new Reader(anExpression));
       valueMap = aValueMap;
     }
@@ -542,7 +572,7 @@ public class ParameterExpander {
     private List parseList() {
       Token token;
       Object expression;
-      List result = new Vector();
+      List result = new ArrayList();
 
       token = scanner.scan();
       if (!(token instanceof LeftParenthesisToken)) {
@@ -557,10 +587,6 @@ public class ParameterExpander {
       do {
         expression = parse();
 
-        if (expression==null) {
-          throw new RuntimeException("expression expected");
-        }
-
         result.add(expression);
 
         token = scanner.scan();
@@ -591,29 +617,19 @@ public class ParameterExpander {
           if (!(token instanceof RightSquareBraceToken))
             throw new RuntimeException("] expected");
 
-          if (currentValue instanceof Map) {
-            currentValue = ((Map) currentValue).get(qualifier);
-          }
-          else {
-            throw new RuntimeException("cannot reference into anything other than a map ('"+qualifier+"')");
-          }
+          currentValue = getObjectField(currentValue, qualifier);
         }
         else if (token instanceof IdentifierToken) {
           scanner.scan();
           qualifier = ((IdentifierToken) token).getName();
 
-          if (currentValue instanceof Map) {
-            currentValue = ((Map) currentValue).get(qualifier);
-          }
-          else {
-            throw new RuntimeException("cannot reference into anything other than a map ('"+qualifier+"')");
-          }
+          currentValue = getObjectField(currentValue, qualifier);
         }
         else if (token instanceof LeftParenthesisToken) {
-          if (currentValue instanceof Generator.GeneratorFunction) {
+          if (currentValue instanceof Generator.Function) {
             parameters = parseList();
             try {
-              currentValue = ((Generator.GeneratorFunction) currentValue).perform(parameters);
+              currentValue = ((Generator.Function) currentValue).perform(parameters);
             }
             catch (GeneratorExc t) {
               throw new RuntimeException(t.getMessage());
@@ -770,29 +786,45 @@ public class ParameterExpander {
       if (aValue instanceof Boolean)
         return ((Boolean) aValue).booleanValue();
 
+      if (aValue instanceof RewindableIterator) {
+        ((RewindableIterator) aValue).rewind();
+      }
+
+      if (aValue instanceof Iterator) {
+        return ((Iterator) aValue).hasNext();
+      }
+
+      if (aValue instanceof List) {
+        return ((List) aValue).size()>0;
+      }
+
+      if (aValue instanceof String) {
+        return ((String) aValue).length()>0;
+      }
+
       return aValue!=null;
     }
 
     private int interpretAsInteger(Object aValue) {
-      if (aValue instanceof Integer)
+      if (aValue instanceof Integer) {
         return ((Integer) aValue).intValue();
-
-      if (aValue instanceof String) {
-        try {
-          return Integer.parseInt((String) aValue);
-        }
-        catch (Throwable t) {
-        }
       }
 
-      throw new RuntimeException("Not an integer");
+      try {
+        return Integer.parseInt(aValue.toString());
+      }
+      catch (NumberFormatException t) {
+        throw new RuntimeException("Not an integer");
+      }
     }
 
     private String interpretAsString(Object aValue) {
+      if (aValue==null)
+        return "";
       if (aValue instanceof String)
         return (String) aValue;
       if (aValue instanceof Integer)
-        return ((Integer) aValue).toString();
+        return aValue.toString();
 
       throw new RuntimeException("Not a string");
     }
@@ -850,22 +882,26 @@ public class ParameterExpander {
       if (aToken instanceof ConcatenateToken)
         return interpretAsString(aValue1) + interpretAsString(aValue2);
 
-      if (aToken instanceof LessThanOrEqualsToken)
+      if (aToken instanceof LessThanOrEqualsToken) {
         return new Boolean(interpretAsInteger(aValue1) <= interpretAsInteger(aValue2));
-      if (aToken instanceof LessThanToken)
+      }
+      if (aToken instanceof LessThanToken) {
         return new Boolean(interpretAsInteger(aValue1) < interpretAsInteger(aValue2));
-      if (aToken instanceof GreaterThanOrEqualsToken)
+      }
+      if (aToken instanceof GreaterThanOrEqualsToken) {
         return new Boolean(interpretAsInteger(aValue1) >= interpretAsInteger(aValue2));
-      if (aToken instanceof GreaterThanToken)
+      }
+      if (aToken instanceof GreaterThanToken) {
         return new Boolean(interpretAsInteger(aValue1) > interpretAsInteger(aValue2));
+      }
 
       throw new RuntimeException("Internal error: unknown binary operator: " + aToken.getClass().getName());
     }
   }
 
   public static class ParameterExpanderExc extends Exc {
-    public ParameterExpanderExc(String msg, Object[] objects) {
-      super(msg, objects);
+    public ParameterExpanderExc(String msg) {
+      super(msg);
     }
   }
 }
\ No newline at end of file