tapestry-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From hls...@apache.org
Subject svn commit: r726253 - in /tapestry/tapestry5/trunk: src/site/apt/ src/site/apt/guide/ tapestry-core/src/main/antlr/org/apache/tapestry5/internal/antlr/ tapestry-core/src/main/java/org/apache/tapestry5/internal/services/ tapestry-core/src/test/java/org/...
Date Sat, 13 Dec 2008 18:28:03 GMT
Author: hlship
Date: Sat Dec 13 10:28:02 2008
New Revision: 726253

URL: http://svn.apache.org/viewvc?rev=726253&view=rev
Log:
TAP5-79: Improve Tapestry's property expression language to include OGNL-like features
- Lists can be built inline inside square brackets

Modified:
    tapestry/tapestry5/trunk/src/site/apt/guide/propexp.apt
    tapestry/tapestry5/trunk/src/site/apt/index.apt
    tapestry/tapestry5/trunk/tapestry-core/src/main/antlr/org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g
    tapestry/tapestry5/trunk/tapestry-core/src/main/antlr/org/apache/tapestry5/internal/antlr/PropertyExpressionParser.g
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/BasePropertyConduit.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/EchoBean.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImplTest.java

Modified: tapestry/tapestry5/trunk/src/site/apt/guide/propexp.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/src/site/apt/guide/propexp.apt?rev=726253&r1=726252&r2=726253&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/src/site/apt/guide/propexp.apt (original)
+++ tapestry/tapestry5/trunk/src/site/apt/guide/propexp.apt Sat Dec 13 10:28:02 2008
@@ -47,7 +47,7 @@
 Grammar
 
 ---
-expression : keyword | rangeOp | constant | propertyChain;
+expression : keyword | rangeOp | constant | propertyChain | list;
 
 keyword : 'null' | 'true' | 'false' | 'this';
 
@@ -62,9 +62,11 @@
               | term;
 
 term :   <identifier>
-     |   <identifier> '(' ')'
-     |   <identifier> '(' expression (',' expression )* ')';
+     |   <identifier> '(' expressionList? ');
 
+list : '[' expressionList? ']';
+
+expressionList : expression (',' expression)*;
      
 ---
 
@@ -74,7 +76,7 @@
   
   * Integers and decimals may have a leading sign ('+' or '-').
 
-  * Constants are in base 10 (sorry, octal and hex not yet supported). Decimals may contain
a decimal point
+  * Constants are in base 10 (octal and hex notation is not yet supported). Decimals may
contain a decimal point
     (exponent notation not yet supported).
 
   * Literal strings are enclosed in single quotes.
@@ -84,7 +86,7 @@
 
   * An identifier by itself is a property name. An identifier with parenthesis is a method
invocation.
 
-  * Property names, method names, and literals are case-insensitive.
+  * Property names, method names, and keywords are case-insensitive.
   
   * 'this' is the root object (i.e., the containing component).
 

Modified: tapestry/tapestry5/trunk/src/site/apt/index.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/src/site/apt/index.apt?rev=726253&r1=726252&r2=726253&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/src/site/apt/index.apt (original)
+++ tapestry/tapestry5/trunk/src/site/apt/index.apt Sat Dec 13 10:28:02 2008
@@ -77,7 +77,7 @@
 
 New And Of Note
 
-  * Property expressions can now invoke methods with parameters.Á
+  * Property expressions have been improved: You can now invoke methods with parameters,
or create a list.
 
   * IoC Service contributions may now be made in terms of classes (that are automatically
instantiated) as well as
     instances.

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/antlr/org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/antlr/org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g?rev=726253&r1=726252&r2=726253&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/antlr/org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g
(original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/antlr/org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g
Sat Dec 13 10:28:02 2008
@@ -50,6 +50,8 @@
 	:	('+'|'-');
 LPAREN 	:	'(';
 RPAREN 	:	')';
+LBRACKET:	'[';
+RBRACKET:	']';
 COMMA	:	',';
 
 fragment QUOTE

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/antlr/org/apache/tapestry5/internal/antlr/PropertyExpressionParser.g
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/antlr/org/apache/tapestry5/internal/antlr/PropertyExpressionParser.g?rev=726253&r1=726252&r2=726253&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/antlr/org/apache/tapestry5/internal/antlr/PropertyExpressionParser.g
(original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/antlr/org/apache/tapestry5/internal/antlr/PropertyExpressionParser.g
Sat Dec 13 10:28:02 2008
@@ -27,6 +27,8 @@
 {	
 	// Parser token representing a method invocation
     	INVOKE;
+    	// A List (top level, or as method parameter)
+    	LIST;
 }
 
 @header
@@ -42,6 +44,7 @@
 	|	rangeOp
 	|	constant
 	|	propertyChain
+	|	list
 	;
 	
 keyword	:	NULL | TRUE | FALSE | THIS;
@@ -74,3 +77,6 @@
 rangeopArg 
 	:	INTEGER
 	|	propertyChain;	
+	
+list	:	LBRACKET RBRACKET -> ^(LIST)
+	|	LBRACKET expressionList RBRACKET -> ^(LIST expressionList);	

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/BasePropertyConduit.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/BasePropertyConduit.java?rev=726253&r1=726252&r2=726253&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/BasePropertyConduit.java
(original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/BasePropertyConduit.java
Sat Dec 13 10:28:02 2008
@@ -15,6 +15,7 @@
 package org.apache.tapestry5.internal.services;
 
 import org.apache.tapestry5.PropertyConduit;
+import org.apache.tapestry5.internal.util.IntegerRange;
 import org.apache.tapestry5.ioc.AnnotationProvider;
 import org.apache.tapestry5.ioc.internal.util.Defense;
 import org.apache.tapestry5.ioc.services.TypeCoercer;
@@ -65,9 +66,9 @@
         return propertyType;
     }
 
-    protected final int toInt(Object value)
+    public final IntegerRange range(int from, int to)
     {
-        return coerce(value, int.class).intValue();
+        return new IntegerRange(from, to);
     }
 
     protected final <T> T coerce(Object value, Class<T> type)

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java?rev=726253&r1=726252&r2=726253&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java
(original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java
Sat Dec 13 10:28:02 2008
@@ -54,6 +54,20 @@
                                                                              new Class[]
{Object.class, Object.class},
                                                                              null);
 
+    private static final Method RANGE;
+
+    static
+    {
+        try
+        {
+            RANGE = BasePropertyConduit.class.getMethod("range", int.class, int.class);
+        }
+        catch (NoSuchMethodException ex)
+        {
+            throw new RuntimeException(ex);
+        }
+    }
+
     private final AnnotationProvider nullAnnotationProvider = new NullAnnotationProvider();
 
     private static class ConstructorParameter
@@ -120,6 +134,27 @@
         String getDescription();
     }
 
+    /**
+     * How are null values in intermdiate terms to be handled?
+     */
+    private enum NullHandling
+    {
+        /**
+         * Add code to check for null and throw exception if null.
+         */
+        FORBID,
+
+        /**
+         * Add code to check for null and short-circuit (i.e., the "?." safe-dereference
operator)
+         */
+        ALLOW,
+
+        /**
+         * Add no null check at all.
+         */
+        IGNORE
+    }
+
     private class GeneratedTerm
     {
         private final Class type;
@@ -289,7 +324,7 @@
             return values.toArray();
         }
 
-        String addInjection(Class fieldType, Object fieldValue)
+        private String addInjection(Class fieldType, Object fieldValue)
         {
             String fieldName =
                     String.format("injected_%s_%d",
@@ -317,7 +352,7 @@
         {
             int type = node.getType();
 
-            return type == IDENTIFIER || type == INVOKE || type == RANGEOP;
+            return type != DEREF && type != SAFEDEREF;
         }
 
         private void createGetRoot()
@@ -377,7 +412,6 @@
 
             classFab.addMethod(Modifier.PRIVATE, sig, navBuilder.toString());
 
-
             createGetterAndSetter(activeType, sig, node);
         }
 
@@ -407,35 +441,70 @@
                     // As currently implemented, RANGEOP can only appear as the top level,
which
                     // means we didn't need the navigate method after all.
 
-                    createRangeOpGetter(navigateMethod, node);
+                    createRangeOpGetter(node);
                     createNoOpSetter();
 
                     conduitPropertyType = IntegerRange.class;
 
                     return;
 
+                case LIST:
+
+                    createListGetter(node);
+                    createNoOpSetter();
+
+                    conduitPropertyType = List.class;
+
+                    return;
 
                 default:
-                    throw unexpectedNodeType(node, IDENTIFIER, INVOKE, RANGEOP);
+                    throw unexpectedNodeType(node, IDENTIFIER, INVOKE, RANGEOP, LIST);
             }
         }
 
-        private void createRangeOpGetter(MethodSignature navigateMethod, Tree node)
+        private void createRangeOpGetter(Tree node)
         {
             BodyBuilder builder = new BodyBuilder().begin();
 
             addRootVariable(builder);
 
-            String fromVar = subexpression(builder, node.getChild(0)).getVariableName();
-            String toVar = subexpression(builder, node.getChild(1)).getVariableName();
+            builder.addln("return %s;", createMethodInvocation(builder, node, 0, RANGE));
+
+            builder.end();
 
-            builder.addln("return new %s(toInt(%s), toInt(%s));", IntegerRange.class.getName(),
fromVar, toVar);
+            classFab.addMethod(Modifier.PUBLIC, GET_SIGNATURE, builder.toString());
+        }
+
+        public void createListGetter(Tree node)
+        {
+            BodyBuilder builder = new BodyBuilder().begin();
+
+            addRootVariable(builder);
+
+            builder.addln("return %s;", createListConstructor(builder, node));
 
             builder.end();
 
             classFab.addMethod(Modifier.PUBLIC, GET_SIGNATURE, builder.toString());
         }
 
+        private String createListConstructor(BodyBuilder builder, Tree node)
+        {
+            String listName = nextVariableName(List.class);
+
+            int count = node.getChildCount();
+
+            builder.addln("java.util.List %s = new java.util.ArrayList(%d);", listName, count);
+
+            for (int i = 0; i < count; i++)
+            {
+                GeneratedTerm generatedTerm = subexpression(builder, node.getChild(i));
+
+                builder.addln("%s.add(($w) %s);", listName, generatedTerm.getVariableName());
+            }
+
+            return listName;
+        }
 
         /**
          * Evalutates the node as a sub expression, storing the result into a new variable,
whose name is returned.
@@ -455,9 +524,9 @@
                 {
                     case INTEGER:
 
-                        Long integerValue = new Long(node.getText());
+                        long integerValue = Long.parseLong(node.getText());
 
-                        previousVariableName = addInjection(Long.class, integerValue);
+                        previousVariableName = addInjection(long.class, integerValue);
                         activeType = Long.class;
 
                         node = null;
@@ -466,8 +535,9 @@
 
                     case DECIMAL:
 
-                        Double decimalValue = new Double(node.getText());
-                        previousVariableName = addInjection(Double.class, decimalValue);
+                        double decimalValue = Double.parseDouble(node.getText());
+
+                        previousVariableName = addInjection(double.class, decimalValue);
                         activeType = Double.class;
 
                         node = null;
@@ -499,7 +569,8 @@
                     case IDENTIFIER:
                     case INVOKE:
 
-                        generated = addAccessForPropertyOrMethod(builder, activeType, node,
previousVariableName, true);
+                        generated = addAccessForPropertyOrMethod(builder, activeType, node,
previousVariableName,
+                                                                 NullHandling.IGNORE);
 
                         previousVariableName = generated.getVariableName();
                         activeType = generated.getType();
@@ -508,8 +579,18 @@
 
                         break;
 
+                    case LIST:
+
+                        previousVariableName = createListConstructor(builder, node);
+                        activeType = List.class;
+
+                        node = null;
+
+                        break;
+
                     default:
-                        throw unexpectedNodeType(node, INTEGER, DECIMAL, STRING, DEREF, SAFEDEREF,
IDENTIFIER, INVOKE);
+                        throw unexpectedNodeType(node, INTEGER, DECIMAL, STRING, DEREF, SAFEDEREF,
IDENTIFIER, INVOKE,
+                                                 LIST);
                 }
             }
 
@@ -589,7 +670,32 @@
             classFab.addMethod(Modifier.PUBLIC, GET_SIGNATURE, builder.toString());
         }
 
-        private String createMethodInvocation(BodyBuilder bodyBuilder, Tree methodInvocation,
Method method)
+
+        /**
+         * Creates a method invocation call for the given node (an INVOKE node).
+         *
+         * @param bodyBuilder may receive new code to define variables for some sub-expressions
+         * @param node        the INVOKE node; child #1 and up are parameter expressions
to the method being invoked
+         * @param method      defines the name and parameter types of the method to invoke
+         * @return method invocation string (the name of the method and any parameters, ready
to be added to a method
+         *         body)
+         */
+        private String createMethodInvocation(BodyBuilder bodyBuilder, Tree node, Method
method)
+        {
+            return createMethodInvocation(bodyBuilder, node, 1, method);
+        }
+
+        /**
+         * Creates a method invocation call for the given node
+         *
+         * @param bodyBuilder may receive new code to define variables for some sub-expressions
+         * @param node        the node containing child nodes for the parameters
+         * @param childOffset the offset to the first parameter (for example, this is 1 for
an INVOKE node)
+         * @param method      defines the name and parameter types of the method to invoke
+         * @return method invocation string (the name of the method and any parameters, ready
to be added to a method
+         *         body)
+         */
+        private String createMethodInvocation(BodyBuilder bodyBuilder, Tree node, int childOffset,
Method method)
         {
             Class[] parameterTypes = method.getParameterTypes();
 
@@ -602,7 +708,7 @@
             {
                 // child(0) is the method name, child(1) is the first parameter, etc.
 
-                GeneratedTerm generatedTerm = subexpression(bodyBuilder, methodInvocation.getChild(i
+ 1));
+                GeneratedTerm generatedTerm = subexpression(bodyBuilder, node.getChild(i
+ childOffset));
                 String variableName = generatedTerm.getVariableName();
 
                 Class actualType = generatedTerm.getType();
@@ -639,8 +745,7 @@
          * Extends the navigate method for a node, which will be a DEREF or SAFEDERF.
          */
         private GeneratedTerm processDerefNode(BodyBuilder builder, Class activeType, Tree
node,
-                                               String previousVariableName
-        )
+                                               String previousVariableName)
         {
             // The first child is the term.
 
@@ -655,7 +760,7 @@
 
 
             return addAccessForPropertyOrMethod(builder, activeType, term, previousVariableName,
-                                                allowNull);
+                                                allowNull ? NullHandling.ALLOW : NullHandling.FORBID);
         }
 
         private String nextVariableName(Class type)
@@ -673,7 +778,7 @@
 
         private GeneratedTerm addAccessForPropertyOrMethod(BodyBuilder builder, Class activeType,
Tree term,
                                                            String previousVariableName,
-                                                           boolean allowNull)
+                                                           NullHandling nullHandling)
         {
             assertNodeType(term, IDENTIFIER, INVOKE);
 
@@ -717,16 +822,21 @@
 
             builder.addln("%s.%s;", previousVariableName, invocation);
 
-            if (allowNull)
+            switch (nullHandling)
             {
-                builder.addln("if (%s == null) return null;", variableName);
-            }
-            else
-            {
-                // Perform a null check on intermediate terms.
-                builder.addln("if (%s == null) %s.nullTerm(\"%s\", \"%s\", $1);",
-                              variableName, PropertyConduitSourceImpl.class.getName(), info.getDescription(),
-                              expression);
+                case ALLOW:
+                    builder.addln("if (%s == null) return null;", variableName);
+                    break;
+
+                case FORBID:
+                    // Perform a null check on intermediate terms.
+                    builder.addln("if (%s == null) %s.nullTerm(\"%s\", \"%s\", $1);",
+                                  variableName, PropertyConduitSourceImpl.class.getName(),
info.getDescription(),
+                                  expression);
+                    break;
+
+                default:
+                    break;
             }
 
             return new GeneratedTerm(wrappedType, variableName);

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/EchoBean.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/EchoBean.java?rev=726253&r1=726252&r2=726253&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/EchoBean.java
(original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/EchoBean.java
Sat Dec 13 10:28:02 2008
@@ -14,6 +14,8 @@
 
 package org.apache.tapestry5.internal.services;
 
+import java.util.List;
+
 public class EchoBean
 {
     public int storedInt;
@@ -78,4 +80,9 @@
     {
         return String.format("%s - %s - %s", before, value, after);
     }
+
+    public List echoList(List input)
+    {
+        return input;
+    }
 }

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImplTest.java?rev=726253&r1=726252&r2=726253&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImplTest.java
(original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImplTest.java
Sat Dec 13 10:28:02 2008
@@ -29,6 +29,7 @@
 import org.testng.annotations.Test;
 
 import java.io.Serializable;
+import java.util.List;
 
 /**
  * Most of the testing occurs inside {@link PropBindingFactoryTest} (due to historical reasons).
@@ -328,4 +329,43 @@
 
         assertEquals(conduit.get(bean), "alpha - Barney - beta");
     }
+
+    @Test
+    public void top_level_list()
+    {
+        PropertyConduit conduit = source.create(EchoBean.class, "[ 1, 2.0, storedString ]");
+        EchoBean bean = new EchoBean();
+
+        bean.setStoredString("Lisa");
+
+        List l = (List) conduit.get(bean);
+
+        assertListsEquals(l, new Long(1), new Double(2.0), "Lisa");
+    }
+
+    @Test
+    public void empty_list()
+    {
+        PropertyConduit conduit = source.create(EchoBean.class, "[  ]");
+        EchoBean bean = new EchoBean();
+
+        bean.setStoredString("Lisa");
+
+        List l = (List) conduit.get(bean);
+
+        assertEquals(l.size(), 0);
+    }
+
+    @Test
+    public void list_as_method_argument()
+    {
+        PropertyConduit conduit = source.create(EchoBean.class, "echoList([ 1, 2.0, storedString
])");
+        EchoBean bean = new EchoBean();
+
+        bean.setStoredString("Bart");
+
+        List l = (List) conduit.get(bean);
+
+        assertListsEquals(l, new Long(1), new Double(2.0), "Bart");
+    }
 }



Mime
View raw message