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.");
}
}