Author: byron
Date: Fri Jan  2 06:55:14 2009
New Revision: 730745

URL: http://svn.apache.org/viewvc?rev=730745&view=rev
Log:
VELOCITY-406 Add handling negative values in index values , to access from the 
end of the foo

Modified:
    
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTIndex.java
    
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTMethod.java
    
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTReference.java
    
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/SimpleNode.java
    velocity/engine/trunk/src/java/org/apache/velocity/util/ClassUtils.java
    velocity/engine/trunk/src/test/org/apache/velocity/test/IndexTestCase.java

Modified: 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTIndex.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTIndex.java?rev=730745&r1=730744&r2=730745&view=diff
==============================================================================
--- 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTIndex.java
 (original)
+++ 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTIndex.java
 Fri Jan  2 06:55:14 2009
@@ -64,15 +64,78 @@
         return data;
     }  
 
+
+    
+    private final static Object[] noParams = {};
+    private final static Class[] noTypes = {};      
+    /**
+     * If argument is an Integer and negative, then return (o.size() - 
argument). 
+     * Otherwise return the original argument.  We use this to calculate the 
true
+     * index of a negative index e.g., $foo[-1]. If no size() method is found 
on the
+     * 'o' object, then we throw an VelocityException.
+     * @param context Used to access the method cache.
+     * @param node  ASTNode used for error reporting.
+     */
+    public static Object adjMinusIndexArg(Object argument, Object o, 
+                               InternalContextAdapter context, SimpleNode node)
+    {
+      if (argument instanceof Integer && ((Integer)argument).intValue() < 0)
+      {
+          // The index value is a negative number, $foo[-1], so we want to 
actually
+          // Index [size - value], so try and call the size method.
+          VelMethod method = ClassUtils.getMethod("size", noParams, noTypes, 
+                             o, context, node, false);
+          if (method == null)
+          {
+              // The object doesn't have a size method, so there is no notion 
of "at the end"
+              throw new VelocityException(
+                "A 'size()' method required for negative value "
+                 + ((Integer)argument).intValue() + " does not exist for class 
'" 
+                 + o.getClass().getName() + "' at " + 
Log.formatFileString(node));
+          }             
+
+          Object size = null;
+          try
+          {
+              size = method.invoke(o, noParams);
+          }
+          catch (Exception e)
+          {
+              throw new VelocityException("Error trying to calls the 'size()' 
method on '"
+                + o.getClass().getName() + "' at " + 
Log.formatFileString(node), e);
+          }
+          
+          int sizeint = 0;          
+          try
+          {
+              sizeint = ((Integer)size).intValue();
+          }
+          catch (ClassCastException e)
+          {
+              // If size() doesn't return an Integer we want to report a 
pretty error
+              throw new VelocityException("Method 'size()' on class '" 
+                  + o.getClass().getName() + "' returned '" + 
size.getClass().getName()
+                  + "' when Integer was expected at " + 
Log.formatFileString(node));
+          }
+          
+          argument = new Integer(sizeint + ((Integer)argument).intValue());
+      }
+      
+      // Nothing to do, return the original argument
+      return argument;
+    }
+    
     public Object execute(Object o, InternalContextAdapter context)
         throws MethodInvocationException
     {
         Object argument = jjtGetChild(0).value(context);
+        // If negative, turn -1 into size - 1
+        argument = adjMinusIndexArg(argument, o, context, this);
         Object [] params = {argument};
         Class[] paramClasses = {argument == null ? null : argument.getClass()};
 
         VelMethod method = ClassUtils.getMethod(methodName, params, 
paramClasses, 
-                                                o, context, this, rsvc, 
strictRef);
+                                                o, context, this, strictRef);
 
         if (method == null) return null;
     
@@ -109,7 +172,7 @@
         {
             String msg = "Error invoking method 'get("
               + (argument == null ? "null" : argument.getClass().getName()) 
-              + ")' in " + o.getClass() 
+              + ")' in " + o.getClass().getName()
               + " at " + Log.formatFileString(this);
             log.error(msg, e);
             throw new VelocityException(msg, e);

Modified: 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTMethod.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTMethod.java?rev=730745&r1=730744&r2=730745&view=diff
==============================================================================
--- 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTMethod.java
 (original)
+++ 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTMethod.java
 Fri Jan  2 06:55:14 2009
@@ -156,7 +156,7 @@
         }
             
         VelMethod method = ClassUtils.getMethod(methodName, params, 
paramClasses, 
-            o, context, this, rsvc, strictRef);
+            o, context, this, strictRef);
         if (method == null) return null;
 
         try

Modified: 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTReference.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTReference.java?rev=730745&r1=730744&r2=730745&view=diff
==============================================================================
--- 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTReference.java
 (original)
+++ 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/ASTReference.java
 Fri Jan  2 06:55:14 2009
@@ -570,7 +570,10 @@
             // the first parameter is the index value and the second is the 
LHS of the set.
           
             String methodName = "put";
-            Object [] params = {astIndex.jjtGetChild(0).value(context), value};
+            Object argument = astIndex.jjtGetChild(0).value(context);
+            // If negative, turn -1 into (size - 1)
+            argument = ASTIndex.adjMinusIndexArg(argument, result, context, 
astIndex);            
+            Object [] params = {argument, value};
             Class[] paramClasses = {params[0] == null ? null : 
params[0].getClass(), 
                                     params[1] == null ? null : 
params[1].getClass()};
             if (params[0] instanceof Integer)
@@ -582,7 +585,7 @@
             }
                    
             VelMethod method = ClassUtils.getMethod(methodName, params, 
paramClasses, 
-                result, context, astIndex, rsvc, strictRef);
+                result, context, astIndex, strictRef);
           
             if (method == null) return false;
             try

Modified: 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/SimpleNode.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/SimpleNode.java?rev=730745&r1=730744&r2=730745&view=diff
==============================================================================
--- 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/SimpleNode.java
 (original)
+++ 
velocity/engine/trunk/src/java/org/apache/velocity/runtime/parser/node/SimpleNode.java
 Fri Jan  2 06:55:14 2009
@@ -76,6 +76,12 @@
     
     protected String templateName;
 
+    
+    public RuntimeServices getRuntimeServices()
+    {
+      return rsvc;
+    }
+    
     /**
      * @param i
      */

Modified: 
velocity/engine/trunk/src/java/org/apache/velocity/util/ClassUtils.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/util/ClassUtils.java?rev=730745&r1=730744&r2=730745&view=diff
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/util/ClassUtils.java 
(original)
+++ velocity/engine/trunk/src/java/org/apache/velocity/util/ClassUtils.java Fri 
Jan  2 06:55:14 2009
@@ -167,16 +167,14 @@
    * @param o Object to introspect for the given method.
    * @param context Context from which the method cache is aquirred
    * @param node ASTNode, used for error reporting.
-   * @param rsvc RuntimeServices used to retrieve the current Uberspector.
    * @param strictRef If no method is found, throw an exception, never return 
null in this case
    * @return VelMethod object if the object is found, null if not matching 
method is found
    */    
   public static VelMethod getMethod(String methodName, Object[] params,
                                     Class[] paramClasses, Object o, 
InternalContextAdapter context,
-                                    SimpleNode node, RuntimeServices rsvc, 
boolean strictRef)
+                                    SimpleNode node, boolean strictRef)
   {
     VelMethod method = null;
-
     try
     {
       /*
@@ -202,7 +200,7 @@
         /*
          * otherwise, do the introspection, and then cache it
          */
-        method = rsvc.getUberspect().getMethod(o, methodName, params,
+        method = node.getRuntimeServices().getUberspect().getMethod(o, 
methodName, params,
            new Info(node.getTemplateName(), node.getLine(), node.getColumn()));
 
         if ((method != null) && (o != null))

Modified: 
velocity/engine/trunk/src/test/org/apache/velocity/test/IndexTestCase.java
URL: 
http://svn.apache.org/viewvc/velocity/engine/trunk/src/test/org/apache/velocity/test/IndexTestCase.java?rev=730745&r1=730744&r2=730745&view=diff
==============================================================================
--- velocity/engine/trunk/src/test/org/apache/velocity/test/IndexTestCase.java 
(original)
+++ velocity/engine/trunk/src/test/org/apache/velocity/test/IndexTestCase.java 
Fri Jan  2 06:55:14 2009
@@ -87,6 +87,8 @@
         assertEvalEquals("BIG TRUE BIG FALSE", "$foo[true] $foo[false]");
         assertEvalEquals("junk foobar ", "$foo[\"junk\"]");
         assertEvalEquals("GOT NULL", "#set($i=$NULL)$boo[$i]");
+        
+        assertEvalEquals("3", "$a[-1]");
     }
 
     public void testIndexSetting()
@@ -97,6 +99,7 @@
         assertEvalEquals("orange","#set($blaa = 
{\"apple\":\"grape\"})#set($blaa[\"apple\"] = \"orange\")$blaa[\"apple\"]");
         assertEvalEquals("null","#set($str[0] = $NULL)#if($str[0] == 
$NULL)null#end");
         assertEvalEquals("null","#set($blaa = 
{\"apple\":\"grape\"})#set($blaa[\"apple\"] = $NULL)#if($blaa[\"apple\"] == 
$NULL)null#end");
+        assertEvalEquals("2112", "#set($a[-1] = 2112)$a[2]");
     }
     
     
@@ -107,6 +110,7 @@
         // Need to fix parse error reporting
         // assertEvalExceptionAt("$boo[blaa]", 1, 6);
         assertEvalExceptionAt("#set($foo[1] = 3)", 1, 10);
+        assertEvalExceptionAt("$a[500]", 1, 3);
     }
     
     


Reply via email to