You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2008/12/12 02:46:12 UTC
svn commit: r725884 - in /tapestry/tapestry5/trunk: src/site/ src/site/apt/
src/site/apt/guide/ tapestry-core/
tapestry-core/src/main/antlr/org/apache/tapestry5/internal/antlr/
tapestry-core/src/main/java/org/apache/tapestry5/internal/antlr/
tapestry-c...
Author: hlship
Date: Thu Dec 11 17:46:11 2008
New Revision: 725884
URL: http://svn.apache.org/viewvc?rev=725884&view=rev
Log:
TAP5-79: Improve Tapestry's property expression language to include OGNL-like features
- Support for invoking methods with parameters
Added:
tapestry/tapestry5/trunk/src/site/apt/guide/propexp.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
- copied, changed from r723593, tapestry/tapestry5/trunk/tapestry-core/src/main/antlr/org/apache/tapestry5/internal/antlr/PropertyExpression.g
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/antlr/
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/antlr/BaseLexer.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/StringSource.java
Removed:
tapestry/tapestry5/trunk/tapestry-core/src/main/antlr/org/apache/tapestry5/internal/antlr/PropertyExpression.g
Modified:
tapestry/tapestry5/trunk/src/site/apt/guide/parameters.apt
tapestry/tapestry5/trunk/src/site/apt/guide/templates.apt
tapestry/tapestry5/trunk/src/site/apt/index.apt
tapestry/tapestry5/trunk/src/site/site.xml
tapestry/tapestry5/trunk/tapestry-core/pom.xml
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/LiteralPropertyConduit.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/PropertyConduitSource.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/internal/services/ServicesStrings.properties
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/bindings/PropBindingFactoryTest.java
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImplTest.java
tapestry/tapestry5/trunk/tapestry-core/tapestry-core.iml
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/InternalUtils.java
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/MethodSignature.java
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/util/InternalUtilsTest.java
Modified: tapestry/tapestry5/trunk/src/site/apt/guide/parameters.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/src/site/apt/guide/parameters.apt?rev=725884&r1=725883&r2=725884&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/src/site/apt/guide/parameters.apt (original)
+++ tapestry/tapestry5/trunk/src/site/apt/guide/parameters.apt Thu Dec 11 17:46:11 2008
@@ -127,13 +127,13 @@
*------------+----------------------------------------------------------------------------------+
| message | Retrieves a value from the component's {{{localization.html}message catalog}}. |
*------------+----------------------------------------------------------------------------------+
-| prop | The name of a property of the containing component to read or update. |
+| prop | A {{{propexp.html}property expression}} to read or update. |
*------------+----------------------------------------------------------------------------------+
| translate | The name of a configured translator. |
*------------+----------------------------------------------------------------------------------+
| validate | A <validator specification> used to create some number of field validators. |
*------------+----------------------------------------------------------------------------------+
-| var | Allows a render variable of the component to be read or updated. | |
+| var | Allows a render variable of the component to be read or updated. |
*------------+----------------------------------------------------------------------------------+
Parameters have a default prefix, usually "prop:", that is used when the prefix is not provided.
@@ -177,54 +177,19 @@
Render variable names are case insensitive.
-Property Bindings
+Property Expression Bindings
- The "prop:" binding prefix indicates a property binding.
-
- The expression for a property binding is a dotted sequence of property names. Simple
- property expressions are just the name of a property, "prop:userName". Complex property
- expression may do a little navigation before getting to the property to read and/or update:
- "prop:userData.name".
-
- In addition to property names, you may also invoke arbitrary methods. The methods must be public,
- return a non-void value, throw no checked exceptions, and take no parameters. To differentiate
- a method name from a property name, you simply append the open and close parenthesis. Thus
- the prior examples could be rewritten as "prop:getUserName()" and "prop:getUserData().getName()".
- Note that when the last term in the expression is a method name, the binding will be read-only,
- rather than read/write.
-
- This feature is most useful for accessing a couple of propertys of standard collection classes
- that aren't named as proper properties, such as Collection.size(), or Map.keySet().
-
- Ever get frustrated, tripping over null pointers inside such an expression? You may use
- "?." instead of "." as the separator. This adds a null check and aborts the expression
- if the term is null. Thus "foo?.bar?.baz" will return null if either foo or bar are null.
- Likewise, updating "foo?.bar?.baz" will turn into a no-op if foo or bar is null.
-
- In addition, a few special cases are also supported.
- In most cases, these special values save you the trouble of adding a "literal:" prefix to
- the value. These special cases are <alternatives> to property expressions.
-
- * "true" and "false" will be converted to booleans.
-
- * "null" will be the value null.
-
- * "this" will be the component itself.
-
- * Simple numeric values are also accepted. These will be parsed into Long or Double objects.
- Ex: "prop:3.14".
+ The "prop:" binding prefix indicates a property expression binding.
+
+ Property expressions are used to link a parameter of a component to a property
+ of its container. Property expressions
+ can navigate a series of properties and/or invoke methods, as well as several
+ other useful patterns. Property expressions have
+ {{{propexp.html}their own documentation}}.
+
+ The default binding prefix in most cases is "prop:", which is why it is usually
+ omitted.
- * A range of integers separated by two periods. Ex: "1..10".
-
- * Literal strings, inside single quotes. Ex: "'Hello World'"
-
- []
-
- In all these cases, excess whitespace is ignored. For the keywords ("true", "false", "this" and
- "null"), case is ignored.
-
- Such values are read only and invariant.
-
Validate Bindings
The "validate:" binding prefix is highly specialized. It allows a short string to be
Added: 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=725884&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/src/site/apt/guide/propexp.apt (added)
+++ tapestry/tapestry5/trunk/src/site/apt/guide/propexp.apt Thu Dec 11 17:46:11 2008
@@ -0,0 +1,97 @@
+ ---
+ Property Expressions
+ ---
+
+Property Expressions
+
+ Tapestry uses property expressions to move data between components. Property expressions
+ are the basis of the {{{parameters.html}component parameters}} and
+ {{{templates.html}template expansions}}.
+
+ A property expression is a string that explains to Tapestry how to navigate from a root object
+ (the containing component) through some number of properties, to a final property that can be
+ read or updated.
+
+ As elsewhere in Tapestry, the names of properties and methods are recognized in a case-insensitive
+ manner.
+
+ The most basic form of a property expression is a simple property name, such as "userName".
+
+ A series of property names may be specified, seperated by commas: "user.name", which is equivalent
+ to either <<<getUser().getName()>>> or <<<getUser().setName()>>>, depending on context.
+
+ The "." is called the "dereference operator". A second operator is "?." or the "safe dereference operator".
+ This works the same as "." except that it allows any of the properties to be null. When reading, a null
+ short-circuits the expression (which returns null). When writing to a property, an intermediate null value will
+ cause the value to be discarded without error.
+
+ In other words, "user?.name" works, even when the user property may be null.
+
+ Property expressions can also reference public methods. Methods may take parameters (or not), but must
+ not return void. When the final term in a property expression is a method, then the property expression
+ is read-only.
+
+ Being able to invoke methods was originally added so that it was possible to access methods such as
+ java.util.Map.size() (which is not named like a property getter method). In Tapestry 5.1, property expressions
+ were extended so that parameters could be passed into methods.
+
+ Parameters to methods are, themselves, property expressions. This means that you can write a property expression
+ that reads a property and passes it as a parameter to a method, and then access a property of the object returns
+ from the method.
+
+Compilation
+
+ Property expressions are compiled to Java classes at runtime; there is no runtime reflection (unlike
+ the OGNL expression language used in prior releases of Tapestry).
+
+Grammar
+
+---
+expression : keyword | rangeOp | constant | propertyChain;
+
+keyword : 'null' | 'true' | 'false' | 'this';
+
+constant : <integer> | <decimal> | <string>;
+
+rangeOp : rangeOpArg '..' rangeOpArg;
+
+rangeOpArg : <integer> | propertyChain;
+
+propertyChain : term '.' propertyChain
+ | term '?.' propertyChain
+ | term;
+
+term : <identifier>
+ | <identifier> '(' ')'
+ | <identifier> '(' expression (',' expression )* ')';
+
+
+---
+
+ Notes:
+
+ * Whitespace is ignored.
+
+ * 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
+ (exponent notation not yet supported).
+
+ * Literal strings are enclosed in single quotes.
+
+ * The rangeOp creates a range object that will iterate between the two values. The upper and lower bounds
+ may be literal integers, or property expressions.
+
+ * 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.
+
+ * 'this' is the root object (i.e., the containing component).
+
+ []
+
+ Method matching is based on method name and number of parameters, but not parameter types. The
+ {{{coercion.html}TypeCoercer}} service is used to convert parameters to the correct type to
+ be passed into the method.
+
+
\ No newline at end of file
Modified: tapestry/tapestry5/trunk/src/site/apt/guide/templates.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/src/site/apt/guide/templates.apt?rev=725884&r1=725883&r2=725884&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/src/site/apt/guide/templates.apt (original)
+++ tapestry/tapestry5/trunk/src/site/apt/guide/templates.apt Thu Dec 11 17:46:11 2008
@@ -236,7 +236,7 @@
Under the covers, expansions are the same as
{{{parameters.html}parameter bindings}}. The default
- binding prefix for expansions is "prop:" (that is, the name of a property), but
+ binding prefix for expansions is "prop:" (that is, the name of a property or a {{{propexp.html}property expression}}), but
other binding prefixes are useful, especially "message:" (to access a localized
message from the component's message catalog).
Modified: tapestry/tapestry5/trunk/src/site/apt/index.apt
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/src/site/apt/index.apt?rev=725884&r1=725883&r2=725884&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/src/site/apt/index.apt (original)
+++ tapestry/tapestry5/trunk/src/site/apt/index.apt Thu Dec 11 17:46:11 2008
@@ -77,6 +77,8 @@
New And Of Note
+ * Property expressions can now invoke methods with parameters.Á
+
* IoC Service contributions may now be made in terms of classes (that are automatically instantiated) as well as
instances.
Modified: tapestry/tapestry5/trunk/src/site/site.xml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/src/site/site.xml?rev=725884&r1=725883&r2=725884&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/src/site/site.xml (original)
+++ tapestry/tapestry5/trunk/src/site/site.xml Thu Dec 11 17:46:11 2008
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
- Copyright 2007 The Apache Software Foundation
+ Copyright 2007, 2008 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.
@@ -96,6 +96,7 @@
<item name="Page Lifecycle" href="guide/lifecycle.html"/>
<item name="Page Navigation" href="guide/pagenav.html"/>
<item name="Persistent Data" href="guide/persist.html"/>
+ <item name="Property Expressions" href="guide/propexp.html"/>
<item name="Project Layout" href="guide/project-layout.html"/>
<item name="Request Processing" href="guide/request.html"/>
<item name="Service Status" href="guide/servicestatus.html"/>
Modified: tapestry/tapestry5/trunk/tapestry-core/pom.xml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/pom.xml?rev=725884&r1=725883&r2=725884&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/pom.xml (original)
+++ tapestry/tapestry5/trunk/tapestry-core/pom.xml Thu Dec 11 17:46:11 2008
@@ -66,6 +66,12 @@
<goals>
<goal>antlr</goal>
</goals>
+ <configuration>
+ <!-- This is a hack so that the parser grammar can locate the tokens file generated
+ by the lexer grammar. -->
+ <libDirectory>target/generated-sources/antlr/org/apache/tapestry5/internal/antlr
+ </libDirectory>
+ </configuration>
</execution>
</executions>
</plugin>
Added: 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=725884&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/antlr/org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/antlr/org/apache/tapestry5/internal/antlr/PropertyExpressionLexer.g Thu Dec 11 17:46:11 2008
@@ -0,0 +1,132 @@
+// Copyright 2008 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.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+lexer grammar PropertyExpressionLexer;
+
+
+options
+{
+ superClass='org.apache.tapestry5.internal.antlr.BaseLexer';
+}
+
+@header
+{
+package org.apache.tapestry5.internal.antlr;
+}
+
+
+// Integer constant
+fragment INTEGER
+ : ;
+
+// Read a property or invoke a method.
+fragment DEREF
+ : ;
+
+// Range operator, ".." between two integers.
+fragment RANGEOP
+ : ;
+
+// Decimal number
+fragment DECIMAL
+ : ;
+
+fragment LETTER
+ : ('a'..'z'|'A'..'Z');
+fragment DIGIT
+ : '0'..'9';
+fragment SIGN
+ : ('+'|'-');
+LPAREN : '(';
+RPAREN : ')';
+COMMA : ',';
+
+fragment QUOTE
+ : '\'';
+
+// Clumsy but effective approach to case-insensitive identifiers.
+
+fragment A
+ : ('a' | 'A');
+fragment E
+ : ('e' | 'E');
+fragment F
+ : ('f' | 'F');
+fragment H
+ : ('h' | 'H');
+fragment I
+ : ('i' | 'I');
+fragment L
+ : ('l' | 'L');
+fragment N
+ : ('n'|'N');
+fragment R
+ : ('r' | 'R');
+fragment S
+ : ('s' | 'S');
+fragment T
+ : ('t' | 'T');
+fragment U
+ : ('u' | 'U');
+
+// Identifiers are case insensitive
+
+NULL : N U L L;
+TRUE : T R U E;
+FALSE : F A L S E;
+THIS : T H I S;
+
+IDENTIFIER
+ : LETTER (LETTER | DIGIT | '_')+;
+
+// The Safe Dereference operator understands not to de-reference through
+// a null.
+
+SAFEDEREF
+ : '?.';
+
+WS : (' '|'\t'|'\n'|'\r')+ { skip(); };
+
+
+// Literal strings are always inside single quotes.
+STRING
+ : QUOTE (options {greedy=false;} : .)* QUOTE { setText(getText().substring(1, getText().length()-1)); };
+
+
+// Special rule that uses parsing tricks to identify numbers and ranges; it's all about
+// the dot ('.').
+// Recognizes:
+// '.' as DEREF
+// '..' as RANGEOP
+// INTEGER (sign? digit+)
+// DECIMAL (sign? digits* . digits+)
+// Has to watch out for embedded rangeop (i.e. "1..10" is not "1." and ".10").
+
+NUMBER_OR_RANGEOP
+ : SIGN? DIGIT+
+ (
+ { input.LA(2) != '.' }? => '.' DIGIT* { $type = DECIMAL; stripLeadingPlus(); }
+ | { $type = INTEGER; stripLeadingPlus(); }
+ )
+
+ | SIGN '.' DIGIT+ { $type = DECIMAL; stripLeadingPlus(); }
+
+ | '.'
+ (
+ DIGIT+ { $type = DECIMAL; stripLeadingPlus();}
+ | '.' {$type = RANGEOP; }
+ | {$type = DEREF; }
+ )
+ ;
+
Copied: tapestry/tapestry5/trunk/tapestry-core/src/main/antlr/org/apache/tapestry5/internal/antlr/PropertyExpressionParser.g (from r723593, tapestry/tapestry5/trunk/tapestry-core/src/main/antlr/org/apache/tapestry5/internal/antlr/PropertyExpression.g)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/antlr/org/apache/tapestry5/internal/antlr/PropertyExpressionParser.g?p2=tapestry/tapestry5/trunk/tapestry-core/src/main/antlr/org/apache/tapestry5/internal/antlr/PropertyExpressionParser.g&p1=tapestry/tapestry5/trunk/tapestry-core/src/main/antlr/org/apache/tapestry5/internal/antlr/PropertyExpression.g&r1=723593&r2=725884&rev=725884&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/antlr/org/apache/tapestry5/internal/antlr/PropertyExpression.g (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/antlr/org/apache/tapestry5/internal/antlr/PropertyExpressionParser.g Thu Dec 11 17:46:11 2008
@@ -12,13 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-grammar PropertyExpression;
+parser grammar PropertyExpressionParser;
options
{
output=AST;
ASTLabelType=CommonTree;
+ tokenVocab=PropertyExpressionLexer;
+ backtrack=true;
}
tokens
@@ -32,127 +34,43 @@
package org.apache.tapestry5.internal.antlr;
}
-@lexer::header
-{
-package org.apache.tapestry5.internal.antlr;
-}
start : expression^ EOF!;
+expression
+ : keyword
+ | rangeOp
+ | constant
+ | propertyChain
+ ;
+
+keyword : NULL | TRUE | FALSE | THIS;
-// Integer constant
-fragment INTEGER
- : ;
-
-// Read a property or invoke a method.
-fragment DEREF
- : ;
-
-// Range operator, ".." between two integers.
-fragment RANGEOP
- : ;
-
-// Decimal number
-fragment DECIMAL
- : ;
-
-fragment LETTER
- : ('a'..'z'|'A'..'Z');
-fragment DIGIT
- : '0'..'9';
-fragment SIGN
- : ('+'|'-');
-LPAREN : '(';
-RPAREN : ')';
-
-fragment QUOTE
- : '\'';
-
-// Clumsy but effective approach to case-insensitive identifiers.
-
-fragment A
- : ('a' | 'A');
-fragment E
- : ('e' | 'E');
-fragment F
- : ('f' | 'F');
-fragment H
- : ('h' | 'H');
-fragment I
- : ('i' | 'I');
-fragment L
- : ('l' | 'L');
-fragment N
- : ('n'|'N');
-fragment R
- : ('r' | 'R');
-fragment S
- : ('s' | 'S');
-fragment T
- : ('t' | 'T');
-fragment U
- : ('u' | 'U');
-
-// Identifiers are case insensitive
-
-NULL : N U L L;
-TRUE : T R U E;
-FALSE : F A L S E;
-THIS : T H I S;
-
-IDENTIFIER
- : LETTER (LETTER | DIGIT | '_')+;
-
-// The Safe Dereference operator understands not to de-reference through
-// a null.
-
-SAFEDEREF
- : '?.';
-
-WS : (' '|'\t'|'\n'|'\r')+ { skip(); };
-
-
-// Literal strings are always inside single quotes.
-STRING
- : QUOTE (options {greedy=false;} : .)* QUOTE { setText(getText().substring(1, getText().length()-1)); };
-
-
-// Special rule that uses parsing tricks to identify numbers and ranges; it's all about
-// the dot ('.').
-// Recognizes:
-// '.' as DEREF
-// '..' as RANGEOP
-// INTEGER (sign? digit+)
-// DECIMAL (sign? digits* . digits+)
-// Has to watch out for embedded rangeop (i.e. "1..10" is not "1." and ".10").
-
-NUMBER_OR_RANGEOP
- : SIGN? DIGIT+
- (
- { input.LA(2) != '.' }? => '.' DIGIT* { $type = DECIMAL; }
- | { $type = INTEGER; }
- )
-
- | SIGN '.' DIGIT+ { $type = DECIMAL; }
+constant: INTEGER| DECIMAL | STRING;
- | '.'
- (
- DIGIT+ { $type = DECIMAL; }
- | '.' {$type = RANGEOP; }
- | {$type = DEREF; }
- )
+propertyChain
+ : term DEREF propertyChain -> ^(DEREF term propertyChain)
+ | term SAFEDEREF propertyChain -> ^(SAFEDEREF term propertyChain)
+ | term
;
-
-expression
- : term DEREF expression -> ^(DEREF term expression)
- | term SAFEDEREF expression -> ^(SAFEDEREF term expression)
- | term;
-
-term : (NULL | TRUE | FALSE | THIS)
- | from=INTEGER RANGEOP to=INTEGER -> ^(RANGEOP $from $to)
- | INTEGER
- | DECIMAL
- | IDENTIFIER
- | STRING
- | id=IDENTIFIER LPAREN RPAREN -> ^(INVOKE $id)
+
+term : IDENTIFIER
+ | methodInvocation
;
+
+methodInvocation
+ : id=IDENTIFIER LPAREN RPAREN -> ^(INVOKE $id)
+ | id=IDENTIFIER LPAREN expressionList RPAREN -> ^(INVOKE $id expressionList)
+ ;
+
+expressionList
+ : expression (COMMA! expression)*
+ ;
+
+rangeOp
+ : from=rangeopArg RANGEOP to=rangeopArg -> ^(RANGEOP $from $to)
+ ;
+
+rangeopArg
+ : INTEGER
+ | propertyChain;
Added: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/antlr/BaseLexer.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/antlr/BaseLexer.java?rev=725884&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/antlr/BaseLexer.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/antlr/BaseLexer.java Thu Dec 11 17:46:11 2008
@@ -0,0 +1,45 @@
+// Copyright 2008 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.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.internal.antlr;
+
+import org.antlr.runtime.CharStream;
+import org.antlr.runtime.Lexer;
+import org.antlr.runtime.RecognizerSharedState;
+
+public abstract class BaseLexer extends Lexer
+{
+ protected BaseLexer()
+ {
+ }
+
+ protected BaseLexer(CharStream charStream,
+ RecognizerSharedState recognizerSharedState)
+ {
+ super(charStream, recognizerSharedState);
+ }
+
+ protected void stripLeadingPlus()
+ {
+ String text = getText();
+
+ // For compatibility with Tapestry 5.0, we need to allow a sign of '+', which Long.parseLong()
+ // doesn't accept. To keep things downstream simple, we eliminate the '+' here.
+
+ if (text.startsWith("+"))
+ {
+ setText(text.substring(1));
+ }
+ }
+}
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=725884&r1=725883&r2=725884&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 Thu Dec 11 17:46:11 2008
@@ -17,6 +17,7 @@
import org.apache.tapestry5.PropertyConduit;
import org.apache.tapestry5.ioc.AnnotationProvider;
import org.apache.tapestry5.ioc.internal.util.Defense;
+import org.apache.tapestry5.ioc.services.TypeCoercer;
import java.lang.annotation.Annotation;
@@ -32,30 +33,45 @@
private final String description;
- public BasePropertyConduit(Class propertyType, AnnotationProvider annotationProvider, String description)
+ private final TypeCoercer typeCoercer;
+
+ public BasePropertyConduit(Class propertyType, AnnotationProvider annotationProvider, String description,
+ TypeCoercer typeCoercer)
{
Defense.notNull(propertyType, "propertyType");
Defense.notNull(annotationProvider, "annotationProvider");
Defense.notBlank(description, "description");
+ Defense.notNull(typeCoercer, "typeCoercer");
this.propertyType = propertyType;
this.annotationProvider = annotationProvider;
this.description = description;
+ this.typeCoercer = typeCoercer;
}
@Override
- public String toString()
+ public final String toString()
{
return description;
}
- public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
+ public final <T extends Annotation> T getAnnotation(Class<T> annotationClass)
{
return annotationProvider.getAnnotation(annotationClass);
}
- public Class getPropertyType()
+ public final Class getPropertyType()
{
return propertyType;
}
+
+ protected final int toInt(Object value)
+ {
+ return coerce(value, int.class).intValue();
+ }
+
+ protected final <T> T coerce(Object value, Class<T> type)
+ {
+ return typeCoercer.coerce(value, type);
+ }
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LiteralPropertyConduit.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LiteralPropertyConduit.java?rev=725884&r1=725883&r2=725884&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LiteralPropertyConduit.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/LiteralPropertyConduit.java Thu Dec 11 17:46:11 2008
@@ -15,6 +15,7 @@
package org.apache.tapestry5.internal.services;
import org.apache.tapestry5.ioc.AnnotationProvider;
+import org.apache.tapestry5.ioc.services.TypeCoercer;
/**
* A PropertyConduit for a literal value in an expression, such as a number, or "true", "false" or "null".
@@ -24,9 +25,10 @@
private final Object value;
public LiteralPropertyConduit(Class propertyType, AnnotationProvider annotationProvider, String description,
+ TypeCoercer typeCoercer,
Object value)
{
- super(propertyType, annotationProvider, description);
+ super(propertyType, annotationProvider, description, typeCoercer);
this.value = value;
}
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=725884&r1=725883&r2=725884&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 Thu Dec 11 17:46:11 2008
@@ -25,6 +25,7 @@
import org.apache.tapestry5.internal.util.IntegerRange;
import org.apache.tapestry5.internal.util.MultiKey;
import org.apache.tapestry5.ioc.AnnotationProvider;
+import org.apache.tapestry5.ioc.internal.NullAnnotationProvider;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.ioc.internal.util.Defense;
import org.apache.tapestry5.ioc.internal.util.GenericsUtils;
@@ -53,6 +54,39 @@
new Class[] {Object.class, Object.class},
null);
+ private final AnnotationProvider nullAnnotationProvider = new NullAnnotationProvider();
+
+ private static class ConstructorParameter
+ {
+ private final String fieldName;
+
+ private final Class type;
+
+ private final Object value;
+
+ ConstructorParameter(String fieldName, Class type, Object value)
+ {
+ this.fieldName = fieldName;
+ this.type = type;
+ this.value = value;
+ }
+
+ public String getFieldName()
+ {
+ return fieldName;
+ }
+
+ public Class getType()
+ {
+ return type;
+ }
+
+ public Object getValue()
+ {
+ return value;
+ }
+ }
+
/**
* Describes all the gory details of one term (one property or method invocation) from within the expression.
*/
@@ -60,15 +94,15 @@
{
/**
- * The name of the method to invoke to read the property value, or null.
+ * The method to invoke to read the property value, or null.
*/
- String getReadMethodName();
+ Method getReadMethod();
/**
- * The name of the method to invoke to write the property value, or null. Always null for method terms (which
- * are inherently read-only).
+ * The method to invoke to write the property value, or null. Always null for method terms (which are inherently
+ * read-only).
*/
- String getWriteMethodName();
+ Method getWriteMethod();
/**
* The return type of the method, or the type of the property.
@@ -86,10 +120,41 @@
String getDescription();
}
+ private class GeneratedTerm
+ {
+ private final Class type;
+
+ private final String variableName;
+
+ private GeneratedTerm(Class type, String variableName)
+ {
+ this.type = type;
+ this.variableName = variableName;
+ }
+
+ /**
+ * The name of the variable that contains the evaluation of the term.
+ */
+ String getVariableName()
+ {
+ return variableName;
+ }
+
+ /**
+ * The type of the variable (used to determine what methods/properties may be dereferenced).
+ */
+ Class getType()
+ {
+ return type;
+ }
+ }
+
private final PropertyAccess access;
private final ClassFactory classFactory;
+ private final TypeCoercer typeCoercer;
+
/**
* Because of stuff like Hibernate, we sometimes start with a subclass in some inaccessible class loader and need to
* work up to a base class from a common class loader.
@@ -120,11 +185,11 @@
}
};
- private final PropertyConduit literalTrue = newLiteralConduit(Boolean.class, true);
+ private final PropertyConduit literalTrue;
- private final PropertyConduit literalFalse = newLiteralConduit(Boolean.class, false);
+ private final PropertyConduit literalFalse;
- private final PropertyConduit literalNull = newLiteralConduit(Void.class, null);
+ private final PropertyConduit literalNull;
/**
@@ -132,7 +197,7 @@
*/
class PropertyConduitBuilder
{
- private final Class rootClass;
+ private final Class rootType;
private final ClassFab classFab;
@@ -142,11 +207,19 @@
private Class conduitPropertyType;
- private AnnotationProvider annotationProvider;
+ private AnnotationProvider annotationProvider = nullAnnotationProvider;
+
+ // Used to create unique variable names.
+
+ private int variableIndex = 0;
- PropertyConduitBuilder(Class rootClass, String expression, Tree tree)
+ private final List<ConstructorParameter> parameters = CollectionFactory.newList();
+
+ private final BodyBuilder navBuilder = new BodyBuilder();
+
+ PropertyConduitBuilder(Class rootType, String expression, Tree tree)
{
- this.rootClass = rootClass;
+ this.rootType = rootType;
this.expression = expression;
this.tree = tree;
@@ -159,18 +232,13 @@
{
createAccessors();
- classFab.addConstructor(new Class[] {Class.class, AnnotationProvider.class, String.class}, null,
- "super($$);");
-
- String description = String.format("PropertyConduit[%s %s]", rootClass.getName(), expression);
+ Object[] parameters = createConstructor();
Class conduitClass = classFab.createClass();
try
{
- return (PropertyConduit) conduitClass.getConstructors()[0].newInstance(conduitPropertyType,
- annotationProvider,
- description);
+ return (PropertyConduit) conduitClass.getConstructors()[0].newInstance(parameters);
}
catch (Exception ex)
{
@@ -178,6 +246,63 @@
}
}
+ private Object[] createConstructor()
+ {
+ List<Class> types = CollectionFactory.newList();
+
+ // $1, $2, $3, $4 ...
+
+ types.add(Class.class);
+ types.add(AnnotationProvider.class);
+ types.add(String.class);
+ types.add(TypeCoercer.class);
+
+ List<Object> values = CollectionFactory.newList();
+
+ values.add(conduitPropertyType);
+ values.add(annotationProvider);
+ values.add(String.format("PropertyConduit[%s %s]", rootType.getName(), expression));
+ values.add(typeCoercer);
+
+ BodyBuilder builder = new BodyBuilder().begin();
+
+ builder.addln("super($1,$2,$3,$4);");
+
+ int index = 5;
+
+ for (ConstructorParameter p : parameters)
+ {
+ types.add(p.getType());
+ values.add(p.getValue());
+
+ builder.addln("%s = $%d;", p.getFieldName(), index++);
+ }
+
+
+ builder.end();
+
+ Class[] arrayOfTypes = types.toArray(new Class[0]);
+
+
+ classFab.addConstructor(arrayOfTypes, null, builder.toString());
+
+ return values.toArray();
+ }
+
+ String addInjection(Class fieldType, Object fieldValue)
+ {
+ String fieldName =
+ String.format("injected_%s_%d",
+ toSimpleName(fieldType),
+ parameters.size());
+
+ classFab.addField(fieldName, Modifier.PRIVATE | Modifier.FINAL, fieldType);
+
+ parameters.add(new ConstructorParameter(fieldName, fieldType, fieldValue));
+
+ return fieldName;
+ }
+
private void createNoOp(ClassFab classFab, MethodSignature signature, String format, Object... values)
{
@@ -190,84 +315,228 @@
private boolean isLeaf(Tree node)
{
- return node.getType() == IDENTIFIER || node.getType() == INVOKE;
+ int type = node.getType();
+
+ return type == IDENTIFIER || type == INVOKE || type == RANGEOP;
}
- private void createAccessors()
+ private void createGetRoot()
{
BodyBuilder builder = new BodyBuilder().begin();
- builder.addln("%s root = (%<s) $1;", ClassFabUtils.toJavaClassName(rootClass));
+ builder.addln("%s root = (%<s) $1;", ClassFabUtils.toJavaClassName(rootType));
builder.addln(
"if (root == null) throw new NullPointerException(\"Root object of property expression '%s' is null.\");",
expression);
- String previousVariableName = "root";
- Class activeType = rootClass;
+ builder.addln("return root;");
+
+ builder.end();
+
+ MethodSignature sig = new MethodSignature(rootType, "getRoot", new Class[] {Object.class}, null);
+
+ classFab.addMethod(Modifier.PRIVATE, sig, builder.toString());
+ }
+
+ private void addRootVariable(BodyBuilder builder)
+ {
+ builder.addln("%s root = getRoot($1);",
+ ClassFabUtils.toJavaClassName(rootType));
+ }
+
+ private void createAccessors()
+ {
+ createGetRoot();
- int step = 0;
+ navBuilder.begin();
+
+ String previousVariableName = "$1";
+ Class activeType = rootType;
Tree node = tree;
while (!isLeaf(node))
{
- assertNodeType(node, DEREF, SAFEDEREF);
+ GeneratedTerm term = processDerefNode(navBuilder, activeType, node, previousVariableName);
+
+ activeType = term.getType();
+
+ previousVariableName = term.getVariableName();
- step++;
+ // Second term is the continuation, possibly another chained DEREF, etc.
+ node = node.getChild(1);
+ }
- String variableName = "step" + step;
+ navBuilder.addln("return %s;", previousVariableName);
- activeType = addDereference(builder, activeType, node, previousVariableName, variableName);
+ navBuilder.end();
- previousVariableName = variableName;
+ MethodSignature sig = new MethodSignature(activeType, "navigate", new Class[] {rootType},
+ null);
- // Second term is the continuation, possibly another chained DEREF, etc.,
- // or at the end of the expression, an IDENTIFIER or INVOKE
- node = node.getChild(1);
+ classFab.addMethod(Modifier.PRIVATE, sig, navBuilder.toString());
+
+
+ createGetterAndSetter(activeType, sig, node);
+ }
+
+ private void createGetterAndSetter(Class activeType, MethodSignature navigateMethod, Tree node)
+ {
+ switch (node.getType())
+ {
+ case IDENTIFIER:
+ case INVOKE:
+
+ // So, a this point, we have the navigation method written and it covers all but the terminal
+ // de-reference. node is an IDENTIFIER or INVOKE. We're ready to use the navigation
+ // method to implement get() and set().
+
+ ExpressionTermInfo info = infoForPropertyOrMethod(activeType, node);
+
+ createSetter(navigateMethod, info);
+ createGetter(navigateMethod, node, info);
+
+ conduitPropertyType = info.getType();
+ annotationProvider = info;
+
+ return;
+
+ case RANGEOP:
+
+ // 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);
+ createNoOpSetter();
+
+ conduitPropertyType = IntegerRange.class;
+
+ return;
+
+
+ default:
+ throw unexpectedNodeType(node, IDENTIFIER, INVOKE, RANGEOP);
}
+ }
+
+ private void createRangeOpGetter(MethodSignature navigateMethod, Tree node)
+ {
+ BodyBuilder builder = new BodyBuilder().begin();
+
+ addRootVariable(builder);
- assertNodeType(node, IDENTIFIER, INVOKE);
+ String fromVar = subexpression(builder, node.getChild(0)).getVariableName();
+ String toVar = subexpression(builder, node.getChild(1)).getVariableName();
- builder.addln("return %s;", previousVariableName);
+ builder.addln("return new %s(toInt(%s), toInt(%s));", IntegerRange.class.getName(), fromVar, toVar);
builder.end();
- MethodSignature sig = new MethodSignature(activeType, "navigate", new Class[] {Object.class},
- null);
+ classFab.addMethod(Modifier.PUBLIC, GET_SIGNATURE, builder.toString());
+ }
- classFab.addMethod(Modifier.PRIVATE, sig, builder.toString());
- // So, a this point, we have the navigation method written and it covers all but the terminal
- // de-reference. node is an IDENTIFIER or INVOKE. We're ready to use the navigation
- // method to implement get() and set().
+ /**
+ * Evalutates the node as a sub expression, storing the result into a new variable, whose name is returned.
+ *
+ * @param builder to receive generated code
+ * @param node root of tree of nodes to be evaluated
+ * @return name of variable containing expression
+ */
+ private GeneratedTerm subexpression(BodyBuilder builder, Tree node)
+ {
+ String previousVariableName = "root";
+ Class activeType = rootType;
+
+ while (node != null)
+ {
+ switch (node.getType())
+ {
+ case INTEGER:
+
+ Long integerValue = new Long(node.getText());
+
+ previousVariableName = addInjection(Long.class, integerValue);
+ activeType = Long.class;
+
+ node = null;
+
+ break;
+
+ case DECIMAL:
+
+ Double decimalValue = new Double(node.getText());
+ previousVariableName = addInjection(Double.class, decimalValue);
+ activeType = Double.class;
+
+ node = null;
+
+ break;
- ExpressionTermInfo info = infoForParseNode(activeType, node);
+ case STRING:
- createSetter(sig, info);
- createGetter(sig, info);
+ String stringValue = node.getText();
+ previousVariableName = addInjection(String.class, stringValue);
+ activeType = String.class;
- conduitPropertyType = info.getType();
- annotationProvider = info;
+ node = null;
+
+ break;
+
+ case DEREF:
+ case SAFEDEREF:
+
+ GeneratedTerm generated = processDerefNode(builder, activeType, node, previousVariableName);
+
+ previousVariableName = generated.getVariableName();
+ activeType = generated.getType();
+
+ node = node.getChild(1);
+
+ break;
+
+ case IDENTIFIER:
+ case INVOKE:
+
+ generated = addAccessForPropertyOrMethod(builder, activeType, node, previousVariableName, true);
+
+ previousVariableName = generated.getVariableName();
+ activeType = generated.getType();
+
+ node = null;
+
+ break;
+
+ default:
+ throw unexpectedNodeType(node, INTEGER, DECIMAL, STRING, DEREF, SAFEDEREF, IDENTIFIER, INVOKE);
+ }
+ }
+
+ return new GeneratedTerm(activeType, previousVariableName);
}
+
private void createSetter(MethodSignature navigateMethod,
ExpressionTermInfo info)
{
- String methodName = info.getWriteMethodName();
+ // A write method will only be identified if the info is a writable property.
+ // Other alternatives: a method as the final term, or a read-only property.
- if (methodName == null)
+ Method method = info.getWriteMethod();
+
+ if (method == null)
{
- createNoOp(classFab, SET_SIGNATURE, "Expression '%s' for class %s is read-only.", expression,
- rootClass.getName());
+ createNoOpSetter();
return;
}
BodyBuilder builder = new BodyBuilder().begin();
- builder.addln("%s target = %s($1);",
- ClassFabUtils.toJavaClassName(navigateMethod.getReturnType()),
- navigateMethod.getName());
+ addRootVariable(builder);
+
+ builder.addln("%s target = navigate(root);",
+ ClassFabUtils.toJavaClassName(navigateMethod.getReturnType()));
// I.e. due to ?. operator. The navigate method will already have checked for nulls
// if they are not allowed.
@@ -276,81 +545,163 @@
String propertyTypeName = ClassFabUtils.toJavaClassName(info.getType());
- builder.addln("target.%s(%s);", methodName, ClassFabUtils.castReference("$2", propertyTypeName));
+ builder.addln("target.%s(%s);", method.getName(), ClassFabUtils.castReference("$2", propertyTypeName));
builder.end();
classFab.addMethod(Modifier.PUBLIC, SET_SIGNATURE, builder.toString());
}
+ private void createNoOpSetter()
+ {
+ createNoOp(classFab, SET_SIGNATURE, "Expression '%s' for class %s is read-only.", expression,
+ rootType.getName());
+ }
+
private void createGetter(MethodSignature navigateMethod,
+ Tree node,
ExpressionTermInfo info)
{
- String methodName = info.getReadMethodName();
+ Method method = info.getReadMethod();
- if (methodName == null)
+ if (method == null)
{
createNoOp(classFab, GET_SIGNATURE, "Expression %s for class %s is write-only.", expression,
- rootClass.getName());
+ rootType.getName());
return;
}
BodyBuilder builder = new BodyBuilder().begin();
- builder.addln("%s target = %s($1);", ClassFabUtils.toJavaClassName(navigateMethod.getReturnType()),
- navigateMethod.getName());
+ addRootVariable(builder);
+
+ builder.addln("%s target = navigate(root);", ClassFabUtils.toJavaClassName(navigateMethod.getReturnType()));
// I.e. due to ?. operator. The navigate method will already have checked for nulls
// if they are not allowed.
builder.addln("if (target == null) return null;");
- builder.addln("return ($w) target.%s();", methodName);
+ builder.addln("return ($w) target.%s;", createMethodInvocation(builder, node, method));
builder.end();
classFab.addMethod(Modifier.PUBLIC, GET_SIGNATURE, builder.toString());
}
+ private String createMethodInvocation(BodyBuilder bodyBuilder, Tree methodInvocation, Method method)
+ {
+ Class[] parameterTypes = method.getParameterTypes();
+
+ StringBuilder builder = new StringBuilder();
+
+ builder.append(method.getName());
+ builder.append("(");
+
+ for (int i = 0; i < parameterTypes.length; i++)
+ {
+ // child(0) is the method name, child(1) is the first parameter, etc.
+
+ GeneratedTerm generatedTerm = subexpression(bodyBuilder, methodInvocation.getChild(i + 1));
+ String variableName = generatedTerm.getVariableName();
+
+ Class actualType = generatedTerm.getType();
+
+ Class parameterType = parameterTypes[i];
+
+ if (!parameterType.isAssignableFrom(actualType))
+ {
+ String coerced = nextVariableName(parameterType);
+
+ String call = String.format("coerce(($w) %s, %s)", variableName,
+ addInjection(Class.class, parameterType));
+
+ String parameterTypeName = ClassFabUtils.toJavaClassName(parameterType);
+
+ bodyBuilder.addln("%s %s = %s;",
+ parameterTypeName, coerced, ClassFabUtils.castReference(call, parameterTypeName));
+
+ variableName = coerced;
+ }
+
+ // TODO: Casting, coercing, unwrapping primitives.
+
+ if (i > 0) builder.append(", ");
+
+ builder.append(variableName);
+ }
+
+ return builder.append(")").toString();
+ }
+
+
/**
- * Part of building the navigation method, adds a de-reference (the '.' or '?.' operator).
- *
- * @param builder recieves the code for this step in the expression
- * @param activeType the current type of the expression (this changes with each de-reference, to the
- * type just de-referenced)
- * @param node the DEREF or SAFEDEREF node
- * @param previousVariableName name of local variable holding previous step in expression
- * @param variableName name of variable to be assigned with this step in expression
+ * Extends the navigate method for a node, which will be a DEREF or SAFEDERF.
*/
- private Class addDereference(BodyBuilder builder, Class activeType, Tree node, String previousVariableName,
- String variableName)
- {// The first child is the term.
+ private GeneratedTerm processDerefNode(BodyBuilder builder, Class activeType, Tree node,
+ String previousVariableName
+ )
+ {
+ // The first child is the term.
Tree term = node.getChild(0);
+ boolean allowNull = node.getType() == SAFEDEREF;
+
+
+ // Returns the type of the method/property ... this is the wrapped (i.e. java.lang.Integer) type if
+ // the real type is primitive. It also reflects generics information that may have been associated
+ // with the underlying method.
+
+
+ return addAccessForPropertyOrMethod(builder, activeType, term, previousVariableName,
+ allowNull);
+ }
+
+ private String nextVariableName(Class type)
+ {
+ return String.format("var_%s_%d",
+ toSimpleName(type), variableIndex++);
+ }
+
+
+ private String toSimpleName(Class type)
+ {
+ // TODO: handle arrays types
+ return InternalUtils.lastTerm(type.getName());
+ }
+
+ private GeneratedTerm addAccessForPropertyOrMethod(BodyBuilder builder, Class activeType, Tree term,
+ String previousVariableName,
+ boolean allowNull)
+ {
assertNodeType(term, IDENTIFIER, INVOKE);
// Get info about this property or method.
- ExpressionTermInfo info = infoForParseNode(activeType, term);
+ ExpressionTermInfo info = infoForPropertyOrMethod(activeType, term);
- String methodName = info.getReadMethodName();
+ Method method = info.getReadMethod();
- if (methodName == null)
+ if (method == null)
throw new PropertyExpressionException(
- ServicesMessages.writeOnlyProperty(info.getDescription(), activeType, expression), expression,
+ ServicesMessages.writeOnlyProperty(info.getDescription(), activeType, expression),
+ expression,
null);
- boolean nullable = node.getType() == SAFEDEREF;
-
// If a primitive type, convert to wrapper type
Class termType = info.getType();
- Class wrappedType = ClassFabUtils.getWrapperType(termType);
+ final Class wrappedType = ClassFabUtils.getWrapperType(termType);
+
+ String wrapperTypeName = ClassFabUtils.toJavaClassName(wrappedType);
+
+ final String variableName = nextVariableName(wrappedType);
+
+ String invocation = createMethodInvocation(builder, term, method);
- String termJavaName = ClassFabUtils.toJavaClassName(wrappedType);
- builder.add("%s %s = ", termJavaName, variableName);
+ builder.add("%s %s = ", wrapperTypeName, variableName);
// Casts are needed for primitives, and for the case where
// generics are involved.
@@ -361,25 +712,24 @@
}
else if (info.isCastRequired())
{
- builder.add(" (%s) ", termJavaName);
+ builder.add(" (%s) ", wrapperTypeName);
}
- builder.addln("%s.%s();", previousVariableName, info.getReadMethodName());
+ builder.addln("%s.%s;", previousVariableName, invocation);
- if (nullable)
+ if (allowNull)
{
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\", root);",
+ builder.addln("if (%s == null) %s.nullTerm(\"%s\", \"%s\", $1);",
variableName, PropertyConduitSourceImpl.class.getName(), info.getDescription(),
expression);
}
- activeType = wrappedType;
- return activeType;
+ return new GeneratedTerm(wrappedType, variableName);
}
private void assertNodeType(Tree node, int... expected)
@@ -391,25 +741,36 @@
if (type == e) return;
}
+ throw unexpectedNodeType(node, expected);
+ }
+
+ private RuntimeException unexpectedNodeType(Tree node, int... expected)
+ {
List<String> tokenNames = CollectionFactory.newList();
for (int i = 0; i < expected.length; i++)
tokenNames.add(PropertyExpressionParser.tokenNames[expected[i]]);
String message =
- String.format("Node %s (within expression '%s') was type %s, but was expected (one of) %s.",
- PropertyExpressionParser.tokenNames[type],
+ String.format("Node %s (within expression '%s') was type %s, but was expected to be (one of) %s.",
+ node.toStringTree(),
+ expression,
+ PropertyExpressionParser.tokenNames[node.getType()],
InternalUtils.joinSorted(tokenNames));
- throw new PropertyExpressionException(message, expression, null);
+ return new PropertyExpressionException(message, expression, null);
}
- private ExpressionTermInfo infoForParseNode(Class activeType, Tree node)
+ private ExpressionTermInfo infoForPropertyOrMethod(Class activeType, Tree node)
{
if (node.getType() == INVOKE)
return infoForInvokeNode(activeType, node);
+ return infoForPropertyNode(activeType, node);
+ }
+ private ExpressionTermInfo infoForPropertyNode(Class activeType, Tree node)
+ {
String propertyName = node.getText();
ClassPropertyAdapter classAdapter = access.getAdapter(activeType);
@@ -421,19 +782,14 @@
return new ExpressionTermInfo()
{
- public String getReadMethodName()
+ public Method getReadMethod()
{
- return name(adapter.getReadMethod());
+ return adapter.getReadMethod();
}
- public String getWriteMethodName()
+ public Method getWriteMethod()
{
- return name(adapter.getWriteMethod());
- }
-
- private String name(Method m)
- {
- return m == null ? null : m.getName();
+ return adapter.getWriteMethod();
}
public Class getType()
@@ -462,26 +818,26 @@
{
String methodName = node.getChild(0).getText();
- final String description = methodName + "()";
+ int parameterCount = node.getChildCount() - 1;
try
{
- final Method method = findMethod(activeType, methodName);
+ final Method method = findMethod(activeType, methodName, parameterCount);
if (method.getReturnType().equals(void.class))
throw new PropertyExpressionException(
- ServicesMessages.methodIsVoid(description, activeType, expression), expression, null);
+ ServicesMessages.methodIsVoid(methodName, activeType, expression), expression, null);
final Class genericType = GenericsUtils.extractGenericReturnType(activeType, method);
return new ExpressionTermInfo()
{
- public String getReadMethodName()
+ public Method getReadMethod()
{
- return method.getName();
+ return method;
}
- public String getWriteMethodName()
+ public Method getWriteMethod()
{
return null;
}
@@ -498,7 +854,7 @@
public String getDescription()
{
- return description;
+ return new MethodSignature(method).getUniqueId();
}
public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
@@ -510,16 +866,17 @@
catch (NoSuchMethodException ex)
{
throw new PropertyExpressionException(
- ServicesMessages.methodNotFound(description, activeType, expression), expression, ex);
+ ServicesMessages.methodNotFound(methodName, activeType, expression), expression, ex);
}
}
- private Method findMethod(Class activeType, String methodName) throws NoSuchMethodException
+ private Method findMethod(Class activeType, String methodName, int parameterCount) throws NoSuchMethodException
{
for (Method method : activeType.getMethods())
{
- if (method.getParameterTypes().length == 0 && method.getName().equalsIgnoreCase(methodName))
+ if (method.getParameterTypes().length == parameterCount && method.getName().equalsIgnoreCase(
+ methodName))
return method;
}
@@ -527,15 +884,21 @@
}
}
- public PropertyConduitSourceImpl(PropertyAccess access, @ComponentLayer ClassFactory classFactory)
+ public PropertyConduitSourceImpl(PropertyAccess access, @ComponentLayer ClassFactory classFactory,
+ TypeCoercer typeCoercer)
{
this.access = access;
this.classFactory = classFactory;
+ this.typeCoercer = typeCoercer;
+
+ literalTrue = createLiteralConduit(Boolean.class, true);
+ literalFalse = createLiteralConduit(Boolean.class, false);
+ literalNull = createLiteralConduit(Void.class, null);
}
public PropertyConduit create(Class rootClass, String expression)
{
- Defense.notNull(rootClass, "rootClass");
+ Defense.notNull(rootClass, "rootType");
Defense.notBlank(expression, "expression");
Class effectiveClass = toEffectiveClass(rootClass);
@@ -584,8 +947,8 @@
* constructor. In a worst-case race condition, we may build two (or more) conduits for the same
* rootClass/expression, and it will get sorted out when the conduit is stored into the cache.
*
- * @param rootClass
- * @param expression
+ * @param rootClass class of root object for expression evaluation
+ * @param expression expression to be evaluated
* @return the conduit
*/
private PropertyConduit build(final Class rootClass, String expression)
@@ -611,32 +974,36 @@
// Leading '+' may screw this up.
// TODO: Singleton instance for "0", maybe "1"?
- return newLiteralConduit(Long.class, new Long(tree.getText()));
+ return createLiteralConduit(Long.class, new Long(tree.getText()));
case DECIMAL:
// Leading '+' may screw this up.
// TODO: Singleton instance for "0.0"?
- return newLiteralConduit(Double.class, new Double(tree.getText()));
+ return createLiteralConduit(Double.class, new Double(tree.getText()));
case STRING:
- return newLiteralConduit(String.class, tree.getText());
+ return createLiteralConduit(String.class, tree.getText());
case RANGEOP:
- // For the moment, we know that RANGEOP must be paired with two INTEGERS.
-
Tree fromNode = tree.getChild(0);
- int from = Integer.parseInt(fromNode.getText());
-
Tree toNode = tree.getChild(1);
+
+ // If the range is defined as integers (not properties, etc.) then
+ // it is possible to calcualte the value here, once, and not build
+ // a new class.
+
+ if (fromNode.getType() != INTEGER || toNode.getType() != INTEGER) break;
+
+ int from = Integer.parseInt(fromNode.getText());
int to = Integer.parseInt(toNode.getText());
IntegerRange ir = new IntegerRange(from, to);
- return newLiteralConduit(IntegerRange.class, ir);
+ return createLiteralConduit(IntegerRange.class, ir);
case THIS:
@@ -670,10 +1037,10 @@
return new PropertyConduitBuilder(rootClass, expression, tree).createInstance();
}
- private <T> PropertyConduit newLiteralConduit(Class<T> type, T value)
+ private <T> PropertyConduit createLiteralConduit(Class<T> type, T value)
{
return new LiteralPropertyConduit(type, invariantAnnotationProvider,
- String.format("LiteralPropertyConduit[%s]", value), value);
+ String.format("LiteralPropertyConduit[%s]", value), typeCoercer, value);
}
private Tree parse(String expression)
@@ -708,7 +1075,7 @@
}
/**
- * May be invoked from the fabricated PropertyConduit instances.
+ * May be invoked from fabricated PropertyConduit instances.
*/
public static void nullTerm(String term, String expression, Object root)
{
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/PropertyConduitSource.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/PropertyConduitSource.java?rev=725884&r1=725883&r2=725884&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/PropertyConduitSource.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/PropertyConduitSource.java Thu Dec 11 17:46:11 2008
@@ -36,9 +36,9 @@
* so despite the name, this method does not always create a <em>new</em> conduit. The cache is cleared if a change
* to component classes is observed.
*
- * @param rootClass the class of the root object to which the expression is applied
+ * @param rootType the type of the root object to which the expression is applied
* @param expression expression to be evaluated on instances of the root class
* @return RuntimeException if the expression is invalid (poorly formed, references non-existent properties, etc.)
*/
- PropertyConduit create(Class rootClass, String expression);
+ PropertyConduit create(Class rootType, String expression);
}
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java?rev=725884&r1=725883&r2=725884&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java Thu Dec 11 17:46:11 2008
@@ -1132,11 +1132,9 @@
return chainBuilder.build(Dispatcher.class, configuration);
}
- public PropertyConduitSource buildPropertyConduitSource(@ComponentLayer ClassFactory componentClassFactory,
+ public PropertyConduitSource buildPropertyConduitSource(@Autobuild PropertyConduitSourceImpl service,
@ComponentClasses InvalidationEventHub hub)
{
- PropertyConduitSourceImpl service = new PropertyConduitSourceImpl(propertyAccess, componentClassFactory);
-
hub.addInvalidationListener(service);
return service;
Modified: tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/internal/services/ServicesStrings.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/internal/services/ServicesStrings.properties?rev=725884&r1=725883&r2=725884&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/internal/services/ServicesStrings.properties (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/internal/services/ServicesStrings.properties Thu Dec 11 17:46:11 2008
@@ -64,8 +64,8 @@
attribute-not-allowed=Element <%s> does not support any attributes.
parameter-element-name-required=The name attribute of the <parameter> element must be specified.
missing-application-state-persistence-strategy=No application state persistence strategy is available with name '%s'. Available strategies: %s.
-method-is-void=Method '%s' returns void (in class %s, within property expression '%s').
-method-not-found=No public method '%s' in class %s (within property expression '%s').
+method-is-void=Method '%s()' returns void (in class %s, within property expression '%s').
+method-not-found=No public method '%s()' in class %s (within property expression '%s').
no-such-property=Class %s does not contain a property named '%s' (within property expression '%s'). Available properties: %s.
write-only-property=Property '%s' of class %s (within property expression '%s') is not readable (it has no read accessor method).
request-exception=Processing of request failed with uncaught exception: %s
Modified: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/bindings/PropBindingFactoryTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/bindings/PropBindingFactoryTest.java?rev=725884&r1=725883&r2=725884&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/bindings/PropBindingFactoryTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/bindings/PropBindingFactoryTest.java Thu Dec 11 17:46:11 2008
@@ -589,6 +589,9 @@
{" 5.", 5d},
{" -100.", -100d},
{" -0.0 ", -0d},
+ {"+50", 50l},
+ {"+7..+20", new IntegerRange(7, 20)},
+ {"+5.5", 5.5d},
{"1..10", new IntegerRange(1, 10)},
{" -20 .. -30 ", new IntegerRange(-20, -30)},
{"0.", 0d},
Added: 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=725884&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/EchoBean.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/EchoBean.java Thu Dec 11 17:46:11 2008
@@ -0,0 +1,81 @@
+// Copyright 2008 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.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.internal.services;
+
+public class EchoBean
+{
+ public int storedInt;
+
+ private double storedDouble;
+
+ private String storedString;
+
+ private StringSource stringSource;
+
+ public StringSource getStringSource()
+ {
+ return stringSource;
+ }
+
+ public void setStringSource(StringSource stringSource)
+ {
+ this.stringSource = stringSource;
+ }
+
+ public int echoInt(int value, int multiplyBy)
+ {
+ return value * multiplyBy;
+ }
+
+ public double echoDouble(double value, double multiplyBy)
+ {
+ return value * multiplyBy;
+ }
+
+ public int getStoredInt()
+ {
+ return storedInt;
+ }
+
+ public void setStoredInt(int storedInt)
+ {
+ this.storedInt = storedInt;
+ }
+
+ public double getStoredDouble()
+ {
+ return storedDouble;
+ }
+
+ public void setStoredDouble(double storedDouble)
+ {
+ this.storedDouble = storedDouble;
+ }
+
+ public String getStoredString()
+ {
+ return storedString;
+ }
+
+ public void setStoredString(String storedString)
+ {
+ this.storedString = storedString;
+ }
+
+ public String echoString(String value, String before, String after)
+ {
+ return String.format("%s - %s - %s", before, value, after);
+ }
+}
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=725884&r1=725883&r2=725884&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 Thu Dec 11 17:46:11 2008
@@ -16,8 +16,10 @@
import org.apache.tapestry5.PropertyConduit;
import org.apache.tapestry5.beaneditor.Validate;
+import org.apache.tapestry5.integration.app1.data.IntegerHolder;
import org.apache.tapestry5.internal.bindings.PropBindingFactoryTest;
import org.apache.tapestry5.internal.test.InternalBaseTestCase;
+import org.apache.tapestry5.internal.util.IntegerRange;
import org.apache.tapestry5.ioc.internal.services.ClassFactoryImpl;
import org.apache.tapestry5.ioc.services.ClassFab;
import org.apache.tapestry5.ioc.services.ClassFactory;
@@ -50,20 +52,51 @@
@Test
public void literal_conduits_have_invariant_annotation()
{
- PropertyConduit normal = source.create(CompositeBean.class, "12345");
+ PropertyConduit pc = source.create(CompositeBean.class, "12345");
- assertNotNull(normal.getAnnotation(Invariant.class));
+ Invariant annotation = pc.getAnnotation(Invariant.class);
+
+ assertNotNull(annotation);
+
+ assertSame(annotation.annotationType(), Invariant.class);
+ }
+
+ @Test
+ public void range_variable_to()
+ {
+ PropertyConduit pc = source.create(IntegerHolder.class, "10..value");
+ IntegerHolder h = new IntegerHolder();
+
+ h.setValue(5);
+
+ IntegerRange ir = (IntegerRange) pc.get(h);
+
+ assertEquals(ir, new IntegerRange(10, 5));
}
@Test
+ public void range_variable_from()
+ {
+ PropertyConduit pc = source.create(IntegerHolder.class, "value..99");
+ IntegerHolder h = new IntegerHolder();
+
+ h.setValue(72);
+
+ IntegerRange ir = (IntegerRange) pc.get(h);
+
+ assertEquals(ir, new IntegerRange(72, 99));
+ }
+
+
+ @Test
public void literal_conduits_are_not_updateable()
{
- PropertyConduit normal = source.create(CompositeBean.class, "12345");
+ PropertyConduit pc = source.create(CompositeBean.class, "12345");
CompositeBean bean = new CompositeBean();
try
{
- normal.set(bean, 42);
+ pc.set(bean, 42);
unreachable();
}
catch (RuntimeException ex)
@@ -244,4 +277,55 @@
assertEquals(annotation.value(), "required");
}
+
+ @Test
+ public void method_invocation_with_integer_arguments()
+ {
+ PropertyConduit conduit = source.create(EchoBean.class, "echoInt(storedInt, 3)");
+ EchoBean bean = new EchoBean();
+
+ for (int i = 0; i < 10; i++)
+ {
+ bean.setStoredInt(i);
+ assertEquals(conduit.get(bean), new Integer(i * 3));
+ }
+ }
+
+ @Test
+ public void method_invocation_with_double_argument()
+ {
+ PropertyConduit conduit = source.create(EchoBean.class, "echoDouble(storedDouble, 2.0)");
+ EchoBean bean = new EchoBean();
+
+ double value = 22. / 7.;
+
+ bean.setStoredDouble(value);
+
+ assertEquals(conduit.get(bean), new Double(2. * value));
+ }
+
+ @Test
+ public void method_invocation_with_string_argument()
+ {
+ PropertyConduit conduit = source.create(EchoBean.class, "echoString(storedString, 'B4', 'AFTER')");
+ EchoBean bean = new EchoBean();
+
+ bean.setStoredString("Moe");
+
+ assertEquals(conduit.get(bean), "B4 - Moe - AFTER");
+ }
+
+ @Test
+ public void method_invocation_using_dereference()
+ {
+ PropertyConduit conduit = source.create(EchoBean.class, "echoString(storedString, stringSource.value, 'beta')");
+ EchoBean bean = new EchoBean();
+
+ StringSource source = new StringSource("alpha");
+
+ bean.setStringSource(source);
+ bean.setStoredString("Barney");
+
+ assertEquals(conduit.get(bean), "alpha - Barney - beta");
+ }
}
Added: tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/StringSource.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/StringSource.java?rev=725884&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/StringSource.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/StringSource.java Thu Dec 11 17:46:11 2008
@@ -0,0 +1,30 @@
+// Copyright 2008 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.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.internal.services;
+
+public class StringSource
+{
+ private final String value;
+
+ public StringSource(String value)
+ {
+ this.value = value;
+ }
+
+ public String getValue()
+ {
+ return value;
+ }
+}
Modified: tapestry/tapestry5/trunk/tapestry-core/tapestry-core.iml
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/tapestry-core.iml?rev=725884&r1=725883&r2=725884&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/tapestry-core.iml (original)
+++ tapestry/tapestry5/trunk/tapestry-core/tapestry-core.iml Thu Dec 11 17:46:11 2008
@@ -10,7 +10,7 @@
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/target/generated-sources/antlr" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target/classes" />
- <excludeFolder url="file://$MODULE_DIR$/target/maven-archiver" />
+ <excludeFolder url="file://$MODULE_DIR$/target/surefire-reports" />
<excludeFolder url="file://$MODULE_DIR$/target/test-classes" />
<excludeFolder url="file://$MODULE_DIR$/test-output" />
</content>
Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/InternalUtils.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/InternalUtils.java?rev=725884&r1=725883&r2=725884&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/InternalUtils.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/InternalUtils.java Thu Dec 11 17:46:11 2008
@@ -104,6 +104,11 @@
return array == null ? 0 : array.length;
}
+ public static int size(Collection collection)
+ {
+ return collection == null ? 0 : collection.size();
+ }
+
/**
* Strips leading "_" and "$" and trailing "_" from the name.
*/
Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/MethodSignature.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/MethodSignature.java?rev=725884&r1=725883&r2=725884&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/MethodSignature.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/MethodSignature.java Thu Dec 11 17:46:11 2008
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007 The Apache Software Foundation
+// Copyright 2006, 2007, 2008 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.
@@ -14,9 +14,8 @@
package org.apache.tapestry5.ioc.services;
-import static org.apache.tapestry5.ioc.internal.util.Defense.notBlank;
-import static org.apache.tapestry5.ioc.internal.util.Defense.notNull;
-import static org.apache.tapestry5.ioc.internal.util.InternalUtils.size;
+import org.apache.tapestry5.ioc.internal.util.Defense;
+import org.apache.tapestry5.ioc.internal.util.InternalUtils;
import java.lang.reflect.Method;
import java.util.Arrays;
@@ -47,8 +46,8 @@
public MethodSignature(Class returnType, String name, Class[] parameterTypes, Class[] exceptionTypes)
{
- this.returnType = notNull(returnType, "returnType");
- this.name = notBlank(name, "name");
+ this.returnType = Defense.notNull(returnType, "returnType");
+ this.name = Defense.notBlank(name, "name");
// Can be null!
this.parameterTypes = parameterTypes;
@@ -96,12 +95,12 @@
hashCode = 31 * hashCode + name.hashCode();
- int count = size(parameterTypes);
+ int count = InternalUtils.size(parameterTypes);
for (int i = 0; i < count; i++)
hashCode = 31 * hashCode + parameterTypes[i].hashCode();
- count = size(exceptionTypes);
+ count = InternalUtils.size(exceptionTypes);
for (int i = 0; i < count; i++)
hashCode = 31 * hashCode + exceptionTypes[i].hashCode();
@@ -134,8 +133,8 @@
private boolean mismatch(Class[] a1, Class[] a2)
{
- int a1Count = size(a1);
- int a2Count = size(a2);
+ int a1Count = InternalUtils.size(a1);
+ int a2Count = InternalUtils.size(a2);
if (a1Count != a2Count) return true;
@@ -160,7 +159,7 @@
buffer.append(name);
buffer.append("(");
- for (int i = 0; i < size(parameterTypes); i++)
+ for (int i = 0; i < InternalUtils.size(parameterTypes); i++)
{
if (i > 0) buffer.append(", ");
@@ -169,7 +168,7 @@
buffer.append(")");
- int _exceptionCount = size(exceptionTypes);
+ int _exceptionCount = InternalUtils.size(exceptionTypes);
String _exceptionNames[] = new String[_exceptionCount];
for (int i = 0; i < _exceptionCount; i++)
{
@@ -190,7 +189,7 @@
}
/**
- * Returns a string consisting of the name of the method and its parameter values. This is similar to {@link
+ * Returns a string consisting of the name of the method and its parameter types. This is similar to {@link
* #toString()}, but omits the return type and information about thrown exceptions. A unique id is used by {@link
* MethodIterator} to identify overlapping methods (methods with the same name and parameter types but with
* different thrown exceptions).
@@ -202,7 +201,7 @@
StringBuilder buffer = new StringBuilder(name);
buffer.append("(");
- for (int i = 0; i < size(parameterTypes); i++)
+ for (int i = 0; i < InternalUtils.size(parameterTypes); i++)
{
if (i > 0) buffer.append(",");
@@ -238,8 +237,8 @@
@SuppressWarnings("unchecked")
private boolean exceptionsEncompass(Class[] otherExceptions)
{
- int ourCount = size(exceptionTypes);
- int otherCount = size(otherExceptions);
+ int ourCount = InternalUtils.size(exceptionTypes);
+ int otherCount = InternalUtils.size(otherExceptions);
// If we have no exceptions, then ours encompass theirs only if they
// have no exceptions, either.
Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/util/InternalUtilsTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/util/InternalUtilsTest.java?rev=725884&r1=725883&r2=725884&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/util/InternalUtilsTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/util/InternalUtilsTest.java Thu Dec 11 17:46:11 2008
@@ -97,7 +97,9 @@
@Test
public void array_size_when_null()
{
- assertEquals(InternalUtils.size(null), 0);
+ Object[] array = null;
+
+ assertEquals(InternalUtils.size(array), 0);
}
@Test
@@ -560,4 +562,16 @@
assertSame(InternalUtils.keys(map), map.keySet());
}
+
+ @Test
+ public void collection_size()
+ {
+ Collection c = null;
+
+ assertEquals(InternalUtils.size(c), 0);
+
+ c = Arrays.asList("moe", "larry", "curly");
+
+ assertEquals(InternalUtils.size(c), 3);
+ }
}