You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by ro...@apache.org on 2011/05/06 05:26:03 UTC

svn commit: r1100036 - in /tapestry/tapestry5/trunk: plastic/src/main/java/org/apache/tapestry5/plastic/ tapestry-core/src/main/antlr/org/apache/tapestry5/internal/antlr/ tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/ tapestry-core/src/...

Author: robertdzeigler
Date: Fri May  6 03:26:02 2011
New Revision: 1100036

URL: http://svn.apache.org/viewvc?rev=1100036&view=rev
Log:
TAP5-1495: Tapestry's property expression language should support map creation
Fix minor typo in plastic documentation
Clarify "parameters" parameter documentation in AbstractLink.

Modified:
    tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilder.java
    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/corelib/base/AbstractLink.java
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/app1/LinkQueryParameters.tml
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CoreBehaviorsTests.java
    tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.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/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilder.java?rev=1100036&r1=1100035&r2=1100036&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilder.java (original)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilder.java Fri May  6 03:26:02 2011
@@ -28,7 +28,7 @@ import java.lang.reflect.Method;
  * try/catch blocks}, is more like a DSL (domain specific language), and is based on callbacks. This looks better in
  * Groovy and will be more reasonable once JDK 1.8 closures are available; in the meantime, it means some deeply nested
  * inner classes, but helps ensure that correct bytecode is generated and helps to limit the amount of bookkeeping is
- * necessary on the part of coce using InstructionBuilder.
+ * necessary on the part of code using InstructionBuilder.
  */
 @SuppressWarnings("rawtypes")
 public interface InstructionBuilder

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=1100036&r1=1100035&r2=1100036&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 Fri May  6 03:26:02 2011
@@ -1,4 +1,4 @@
-// Copyright 2008, 2010 The Apache Software Foundation
+// Copyright 2008, 2010, 2011 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -53,7 +53,10 @@ RPAREN 	:	')';
 LBRACKET:	'[';
 RBRACKET:	']';
 COMMA	:	',';
-BANG    :	'!';
+BANG	:	'!';
+LBRACE	:	'{';
+RBRACE	:	'}';
+COLON	:	':';
 
 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=1100036&r1=1100035&r2=1100036&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 Fri May  6 03:26:02 2011
@@ -1,4 +1,4 @@
-// Copyright 2008, 2009 The Apache Software Foundation
+// Copyright 2008, 2009, 2011 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -30,6 +30,8 @@ tokens
     	INVOKE;
     	// A List (top level, or as method parameter)
     	LIST;
+		//A Map (top level, or as method parameter)
+		MAP;
     	// Not operation (invert a boolean)
     	NOT;
 }
@@ -49,8 +51,9 @@ expression
 	|	propertyChain
 	|	list
 	|	notOp
+	|	map
 	;
-	
+
 keyword	:	NULL | TRUE | FALSE | THIS;
 
 constant:	INTEGER| DECIMAL | STRING;	
@@ -73,7 +76,7 @@ methodInvocation
 expressionList
 	:	expression (COMMA! expression)*
 	;	
-	
+
 rangeOp
 	:	from=rangeopArg  RANGEOP to=rangeopArg -> ^(RANGEOP $from $to)
 	;	
@@ -87,6 +90,16 @@ list	:	LBRACKET RBRACKET -> ^(LIST)
 	|	LBRACKET expressionList RBRACKET -> ^(LIST expressionList)
 	;	
 	
-	
 notOp 	:	BANG expression -> ^(NOT expression)
 	;
+
+map 	:	LBRACE RBRACE -> ^(MAP)
+	|	LBRACE mapEntryList RBRACE -> ^(MAP mapEntryList)
+    ;
+	
+mapEntryList : mapEntry (COMMA! mapEntry)*;
+
+mapEntry :  mapKey COLON! expression;
+	
+mapKey :	keyword | constant | propertyChain;
+	

Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractLink.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractLink.java?rev=1100036&r1=1100035&r2=1100036&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractLink.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/base/AbstractLink.java Fri May  6 03:26:02 2011
@@ -45,7 +45,7 @@ public abstract class AbstractLink imple
 
     /**
      * If specified, the parameters are added to the link as query parameters in key=value fashion.
-     * Both values will be coerced to string using value encoder.
+     * Values will be coerced to string using value encoder; keys should be Strings.
      * @since 5.3
      */
     @Parameter(allowNull = false)

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=1100036&r1=1100035&r2=1100036&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 Fri May  6 03:26:02 2011
@@ -21,6 +21,7 @@ import static org.apache.tapestry5.inter
 import static org.apache.tapestry5.internal.antlr.PropertyExpressionParser.INTEGER;
 import static org.apache.tapestry5.internal.antlr.PropertyExpressionParser.INVOKE;
 import static org.apache.tapestry5.internal.antlr.PropertyExpressionParser.LIST;
+import static org.apache.tapestry5.internal.antlr.PropertyExpressionParser.MAP;
 import static org.apache.tapestry5.internal.antlr.PropertyExpressionParser.NOT;
 import static org.apache.tapestry5.internal.antlr.PropertyExpressionParser.NULL;
 import static org.apache.tapestry5.internal.antlr.PropertyExpressionParser.RANGEOP;
@@ -37,6 +38,7 @@ import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.lang.reflect.Type;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -108,6 +110,11 @@ public class PropertyConduitSourceImpl i
         static final Method ADD = getMethod(ArrayList.class, "add", Object.class);
     }
 
+    static class HashMapMethods
+    {
+        static final Method PUT = getMethod(HashMap.class, "put", Object.class, Object.class);
+    }
+
     private static InstructionBuilderCallback RETURN_NULL = new InstructionBuilderCallback()
     {
         public void doBuild(InstructionBuilder builder)
@@ -450,6 +457,15 @@ public class PropertyConduitSourceImpl i
 
                     return;
 
+                case MAP:
+                    implementMapGetter(node);
+                    implementNoOpSetter();
+
+                    conduitPropertyType = Map.class;
+
+                    return;
+
+
                 case NOT:
                     implementNotOpGetter(node);
                     implementNoOpSetter();
@@ -751,6 +767,9 @@ public class PropertyConduitSourceImpl i
 
                         return implementListConstructor(builder, node);
 
+                    case MAP:
+                        return implementMapConstructor(builder, node);
+
                     case NOT:
 
                         return implementNotExpression(builder, node);
@@ -804,6 +823,46 @@ public class PropertyConduitSourceImpl i
             return ArrayList.class;
         }
 
+        private void implementMapGetter(final Tree mapNode)
+        {
+            plasticClass.introduceMethod(ConduitMethods.GET, new InstructionBuilderCallback()
+            {
+                public void doBuild(InstructionBuilder builder)
+                {
+                    implementMapConstructor(builder, mapNode);
+
+                    builder.returnResult();
+                }
+            });
+        }
+
+        private Type implementMapConstructor(InstructionBuilder builder, Tree mapNode)
+        {
+            int count = mapNode.getChildCount();
+            builder.newInstance(HashMap.class);
+            builder.dupe().loadConstant(count).invokeConstructor(HashMap.class, int.class);
+
+            for (int i = 0; i < count; i+=2)
+            {
+                builder.dupe();
+
+                //build the key:
+                Type keyType = implementSubexpression(builder, null, mapNode.getChild(i));
+                boxIfPrimitive(builder, GenericsUtils.asClass(keyType));
+
+                //and the value:
+                Type valueType = implementSubexpression(builder, null, mapNode.getChild(i+1));
+                boxIfPrimitive(builder, GenericsUtils.asClass(valueType));
+
+                //put the value into the array, then pop off the returned object.
+                builder.invoke(HashMapMethods.PUT).pop();
+
+            }
+
+            return HashMap.class;
+        }
+
+
         private void implementNoOpSetter()
         {
             implementNoOpMethod(ConduitMethods.SET, "Expression '%s' for class %s is read-only.", expression,

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/app1/LinkQueryParameters.tml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/app1/LinkQueryParameters.tml?rev=1100036&r1=1100035&r2=1100036&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/app1/LinkQueryParameters.tml (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/app1/LinkQueryParameters.tml Fri May  6 03:26:02 2011
@@ -13,12 +13,18 @@
         <li><a t:type="eventlink" event="parameterCheck">Event Link With No Parameters</a></li>
         <li><a t:type="eventlink" event="parameterCheck" parameters="emptyParameters">Event Link With Explicitly Empty Parameters</a></li>
         <li><a t:type="eventlink" event="parameterCheck" parameters="nonEmptyParameters">Event Link With Parameters</a></li>
+
+        <li>
+            <a t:type="pagelink" page="LinkQueryParameters" parameters="{'parama': 'valuea', 'paramb': 'valueb'}">
+                Two Element Map
+            </a>
+        </li>
     </ul>
 
     <div id="parametercheck">
         <t:if test="hasParameters">
             <ul>
-                <li t:type="loop" source="parameters" value="paramName" class="qparam">
+                <li t:type="loop" source="parameters" value="paramName" class="${paramName}">
                     ${paramName}: ${paramVal}
                 </li>
             </ul>

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CoreBehaviorsTests.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CoreBehaviorsTests.java?rev=1100036&r1=1100035&r2=1100036&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CoreBehaviorsTests.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/CoreBehaviorsTests.java Fri May  6 03:26:02 2011
@@ -467,19 +467,33 @@ public class CoreBehaviorsTests extends 
         openLinkParameterTest();
 
         clickAndWait("link=Page Link With Parameters");
-        assertText("xpath=(//li[@class='qparam'])[1]", "param1: value1");
-        assertText("xpath=(//li[@class='qparam'])[2]", "param2: 10");
+        assertText("css=li.param1", "param1: value1");
+        assertText("css=li.param2", "param2: 10");
 
         //re-open between checks to make sure there is no "bleedover" between checks.
         openLinkParameterTest();
         clickAndWait("link=Action Link With Parameters");
-        assertText("xpath=(//li[@class='qparam'])[1]", "param1: value1");
-        assertText("xpath=(//li[@class='qparam'])[2]", "param2: 10");
+        assertText("css=li.param1", "param1: value1");
+        assertText("css=li.param2", "param2: 10");
 
         openLinkParameterTest();;
         clickAndWait("link=Event Link With Parameters");
-        assertText("xpath=(//li[@class='qparam'])[1]", "param1: value1");
-        assertText("xpath=(//li[@class='qparam'])[2]", "param2: 10");
+        assertText("css=li.param1", "param1: value1");
+        assertText("css=li.param2", "param2: 10");
+    }
+
+    //TAP5-1495
+    @Test
+    public void map_expression_support()
+    {
+        openLinkParameterTest();
+
+        //more extensive testing done in PropertyConduitSourceImplTest, including multiple key and value types,
+        //access from methods and nested methods, and map as method arguments.
+        //This just provides an integration sanity check.
+        clickAndWait("link=Two Element Map");
+        assertText("css=li.parama", "parama: valuea");
+        assertText("css=li.paramb", "paramb: valueb");
     }
 
     @Test

Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java?rev=1100036&r1=1100035&r2=1100036&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java Fri May  6 03:26:02 2011
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2009, 2010, 2011 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2009, 2010 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.

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=1100036&r1=1100035&r2=1100036&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 Fri May  6 03:26:02 2011
@@ -1,4 +1,4 @@
-// Copyright 2008 The Apache Software Foundation
+// Copyright 2008, 2011 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
 package org.apache.tapestry5.internal.services;
 
 import java.util.List;
+import java.util.Map;
 
 public class EchoBean
 {
@@ -103,4 +104,9 @@ public class EchoBean
     {
         return input;
     }
+
+    public Map echoMap(Map 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=1100036&r1=1100035&r2=1100036&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 Fri May  6 03:26:02 2011
@@ -1,4 +1,4 @@
-// Copyright 2007, 2008, 2009, 2010 The Apache Software Foundation
+// Copyright 2007, 2008, 2009, 2010, 2011 The Apache Software Foundation
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -25,6 +25,7 @@ import org.apache.tapestry5.internal.tes
 import org.apache.tapestry5.internal.util.Holder;
 import org.apache.tapestry5.internal.util.IntegerRange;
 import org.apache.tapestry5.ioc.internal.services.ClassFactoryImpl;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry5.ioc.services.ClassFab;
 import org.apache.tapestry5.ioc.services.ClassFactory;
 import org.apache.tapestry5.services.PropertyConduitSource;
@@ -34,6 +35,7 @@ import org.testng.annotations.Test;
 
 import java.io.Serializable;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Most of the testing occurs inside {@link PropBindingFactoryTest} (due to
@@ -622,6 +624,49 @@ public class PropertyConduitSourceImplTe
     }
 
     @Test
+    public void top_level_map()
+    {
+        PropertyConduit conduit = source.create(EchoBean.class, "{'one': true, 'two': 2.0, stringSource.value: 3, 'four': storedString}");
+        EchoBean bean = new EchoBean();
+
+        bean.setStoredString("four");
+        bean.setStringSource(new StringSource("three"));
+
+        Map actual = (Map) conduit.get(bean);
+        assertEquals(actual.get("one"), true);
+        assertEquals(actual.get("two"), 2.0);
+        assertEquals(actual.get("three"), 3L);
+        assertEquals(actual.get("four"), "four");
+    }
+
+    @Test
+    public void empty_map()
+    {
+        PropertyConduit conduit = source.create(EchoBean.class, "{ }");
+        EchoBean bean = new EchoBean();
+        Map m = (Map) conduit.get(bean);
+
+        assertTrue(m.isEmpty());
+
+    }
+
+    @Test
+    public void map_as_method_argument()
+    {
+        PropertyConduit conduit = source.create(EchoBean.class, "echoMap({ 1: 'one', 2.0: 'two', storedString: stringSource.value })");
+        EchoBean bean = new EchoBean();
+
+        bean.setStoredString( "3" );
+        bean.setStringSource(new StringSource("three"));
+
+        Map m = (Map) conduit.get(bean);
+        assertEquals("one", m.get(1L));
+        assertEquals("two", m.get(2.0));
+        assertEquals("three", m.get("3"));
+
+    }
+
+    @Test
     public void not_operator()
     {
         PropertyConduit conduit = source.create(IntegerHolder.class, "! value");
@@ -678,8 +723,10 @@ public class PropertyConduitSourceImplTe
         }
         catch (RuntimeException ex)
         {
+            //note that addition of map support changed how this expression was parsed such that the error is now
+            //reported at character 8, (, rather than 0: getValue(.
             assertEquals(ex.getMessage(),
-                    "Error parsing property expression 'getValue(': line 1:0 no viable alternative at input 'getValue'.");
+                    "Error parsing property expression 'getValue(': line 1:8 no viable alternative at input '('.");
         }
     }
 
@@ -688,13 +735,13 @@ public class PropertyConduitSourceImplTe
     {
         try
         {
-            source.create(IntegerHolder.class, "fred {");
+            source.create(IntegerHolder.class, "fred #");
             unreachable();
         }
         catch (RuntimeException ex)
         {
             assertEquals(ex.getMessage(),
-                    "Error parsing property expression 'fred {': Unable to parse input at character position 6.");
+                    "Error parsing property expression 'fred #': Unable to parse input at character position 6.");
         }
     }