Browse Source

Temporary addition of spring-el to security source tree until binary is available

Luke Taylor 17 năm trước cách đây
mục cha
commit
0dd82cb91a
100 tập tin đã thay đổi với 15053 bổ sung0 xóa
  1. 46 0
      spring-el/pom.xml
  2. 44 0
      spring-el/src/main/java/org/springframework/expression/AccessException.java
  3. 57 0
      spring-el/src/main/java/org/springframework/expression/CacheablePropertyAccessor.java
  4. 43 0
      spring-el/src/main/java/org/springframework/expression/ConstructorExecutor.java
  5. 39 0
      spring-el/src/main/java/org/springframework/expression/ConstructorResolver.java
  6. 87 0
      spring-el/src/main/java/org/springframework/expression/EvaluationContext.java
  7. 99 0
      spring-el/src/main/java/org/springframework/expression/EvaluationException.java
  8. 110 0
      spring-el/src/main/java/org/springframework/expression/Expression.java
  9. 56 0
      spring-el/src/main/java/org/springframework/expression/ExpressionParser.java
  10. 43 0
      spring-el/src/main/java/org/springframework/expression/MethodExecutor.java
  11. 38 0
      spring-el/src/main/java/org/springframework/expression/MethodResolver.java
  12. 26 0
      spring-el/src/main/java/org/springframework/expression/Operation.java
  13. 37 0
      spring-el/src/main/java/org/springframework/expression/OperatorOverloader.java
  14. 89 0
      spring-el/src/main/java/org/springframework/expression/ParseException.java
  15. 55 0
      spring-el/src/main/java/org/springframework/expression/ParserContext.java
  16. 88 0
      spring-el/src/main/java/org/springframework/expression/PropertyAccessor.java
  17. 40 0
      spring-el/src/main/java/org/springframework/expression/PropertyReaderExecutor.java
  18. 40 0
      spring-el/src/main/java/org/springframework/expression/PropertyWriterExecutor.java
  19. 46 0
      spring-el/src/main/java/org/springframework/expression/TypeComparator.java
  20. 52 0
      spring-el/src/main/java/org/springframework/expression/TypeConverter.java
  21. 38 0
      spring-el/src/main/java/org/springframework/expression/TypeLocator.java
  22. 54 0
      spring-el/src/main/java/org/springframework/expression/TypeUtils.java
  23. 78 0
      spring-el/src/main/java/org/springframework/expression/common/CompositeStringExpression.java
  24. 24 0
      spring-el/src/main/java/org/springframework/expression/common/DefaultNonTemplateParserContext.java
  25. 24 0
      spring-el/src/main/java/org/springframework/expression/common/DefaultTemplateParserContext.java
  26. 55 0
      spring-el/src/main/java/org/springframework/expression/common/ExpressionUtils.java
  27. 65 0
      spring-el/src/main/java/org/springframework/expression/common/LiteralExpression.java
  28. 115 0
      spring-el/src/main/java/org/springframework/expression/common/TemplateAwareExpressionParser.java
  29. 202 0
      spring-el/src/main/java/org/springframework/expression/spel/ExpressionState.java
  30. 108 0
      spring-el/src/main/java/org/springframework/expression/spel/SpelException.java
  31. 153 0
      spring-el/src/main/java/org/springframework/expression/spel/SpelExpression.java
  32. 91 0
      spring-el/src/main/java/org/springframework/expression/spel/SpelExpressionParser.java
  33. 163 0
      spring-el/src/main/java/org/springframework/expression/spel/SpelMessages.java
  34. 58 0
      spring-el/src/main/java/org/springframework/expression/spel/SpelUtilities.java
  35. 54 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/Assign.java
  36. 40 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/BooleanLiteral.java
  37. 120 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java
  38. 317 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java
  39. 46 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/Dot.java
  40. 147 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java
  41. 41 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/Identifier.java
  42. 156 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/Indexer.java
  43. 39 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/IntLiteral.java
  44. 101 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/Literal.java
  45. 39 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/LongLiteral.java
  46. 156 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/MethodReference.java
  47. 36 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/NullLiteral.java
  48. 70 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/Operator.java
  49. 65 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorAnd.java
  50. 73 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorBetween.java
  51. 63 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorDivide.java
  52. 58 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorEquality.java
  53. 58 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorGreaterThan.java
  54. 58 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorGreaterThanOrEqual.java
  55. 58 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorInequality.java
  56. 64 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorInstanceof.java
  57. 58 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorLessThan.java
  58. 58 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorLessThanOrEqual.java
  59. 75 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java
  60. 95 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorMinus.java
  61. 63 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorModulus.java
  62. 86 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorMultiply.java
  63. 51 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorNot.java
  64. 63 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorOr.java
  65. 88 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorPlus.java
  66. 46 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/Placeholder.java
  67. 98 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/Projection.java
  68. 245 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java
  69. 69 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/QualifiedIdentifier.java
  70. 34 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/RealLiteral.java
  71. 146 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/Selection.java
  72. 126 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/SpelNode.java
  73. 42 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/StringLiteral.java
  74. 66 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/Ternary.java
  75. 64 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/TypeReference.java
  76. 70 0
      spring-el/src/main/java/org/springframework/expression/spel/ast/VariableReference.java
  77. 268 0
      spring-el/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g
  78. 74 0
      spring-el/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.tokens
  79. 2687 0
      spring-el/src/main/java/org/springframework/expression/spel/generated/SpringExpressionsLexer.java
  80. 4105 0
      spring-el/src/main/java/org/springframework/expression/spel/generated/SpringExpressionsParser.java
  81. 142 0
      spring-el/src/main/java/org/springframework/expression/spel/generated/SpringExpressions__.g
  82. 39 0
      spring-el/src/main/java/org/springframework/expression/spel/internal/EmptySpelNode.java
  83. 35 0
      spring-el/src/main/java/org/springframework/expression/spel/internal/InternalELException.java
  84. 33 0
      spring-el/src/main/java/org/springframework/expression/spel/internal/KeyValuePair.java
  85. 172 0
      spring-el/src/main/java/org/springframework/expression/spel/internal/SpelTreeAdaptor.java
  86. 81 0
      spring-el/src/main/java/org/springframework/expression/spel/internal/SpringExpressionsLexerExtender.java
  87. 87 0
      spring-el/src/main/java/org/springframework/expression/spel/internal/SpringExpressionsParserExtender.java
  88. 103 0
      spring-el/src/main/java/org/springframework/expression/spel/internal/TypeCode.java
  89. 76 0
      spring-el/src/main/java/org/springframework/expression/spel/internal/Utils.java
  90. 57 0
      spring-el/src/main/java/org/springframework/expression/spel/internal/VariableScope.java
  91. 35 0
      spring-el/src/main/java/org/springframework/expression/spel/internal/WrappedExpressionException.java
  92. 74 0
      spring-el/src/main/java/org/springframework/expression/spel/reflection/ReflectionConstructorExecutor.java
  93. 66 0
      spring-el/src/main/java/org/springframework/expression/spel/reflection/ReflectionConstructorResolver.java
  94. 70 0
      spring-el/src/main/java/org/springframework/expression/spel/reflection/ReflectionMethodExecutor.java
  95. 63 0
      spring-el/src/main/java/org/springframework/expression/spel/reflection/ReflectionMethodResolver.java
  96. 57 0
      spring-el/src/main/java/org/springframework/expression/spel/reflection/ReflectionPropertyReaderExecutor.java
  97. 21 0
      spring-el/src/main/java/org/springframework/expression/spel/reflection/ReflectionPropertyReaderExecutorForArrayLength.java
  98. 223 0
      spring-el/src/main/java/org/springframework/expression/spel/reflection/ReflectionPropertyResolver.java
  99. 58 0
      spring-el/src/main/java/org/springframework/expression/spel/reflection/ReflectionPropertyWriterExecutor.java
  100. 562 0
      spring-el/src/main/java/org/springframework/expression/spel/reflection/ReflectionUtils.java

+ 46 - 0
spring-el/pom.xml

@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.springframework.security</groupId>
+        <artifactId>spring-security-parent</artifactId>
+        <version>2.5.0-SNAPSHOT</version>
+    </parent>
+	<artifactId>spring-el-security</artifactId>
+	<packaging>jar</packaging>
+	<name>Spring Expression Language for Security</name>
+	<dependencies>
+		<dependency>
+			<groupId>commons-logging</groupId>
+			<artifactId>commons-logging</artifactId>
+			<version>1.1.1</version>
+			<optional>true</optional>
+		</dependency>
+		<dependency>
+			<groupId>org.antlr</groupId>
+			<artifactId>antlr</artifactId>
+			<version>3.0.1</version>
+		</dependency>
+<!--		
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-core</artifactId>
+            <version>2.5.5</version>
+        </dependency>
+-->        
+	</dependencies>
+	<build>
+        <plugins> 	    
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>2.4.2</version>
+                <configuration>
+                    <excludes>
+                        <exclude>**/*Tests.class</exclude>
+                    </excludes>
+                </configuration>                    
+    	    </plugin>
+	    </plugins> 
+	</build>
+</project>

+ 44 - 0
spring-el/src/main/java/org/springframework/expression/AccessException.java

@@ -0,0 +1,44 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression;
+
+/**
+ * An AccessException is thrown by an accessor if it has an unexpected problem.
+ * 
+ * @author Andy Clement
+ */
+public class AccessException extends Exception {
+
+	/**
+	 * Create an AccessException with a specific message and cause.
+	 * 
+	 * @param message the message
+	 * @param cause the cause
+	 */
+	public AccessException(String message, Exception cause) {
+		super(message, cause);
+	}
+
+	/**
+	 * Create an AccessException with a specific message.
+	 * 
+	 * @param message the message
+	 */
+	public AccessException(String message) {
+		super(message);
+	}
+
+}

+ 57 - 0
spring-el/src/main/java/org/springframework/expression/CacheablePropertyAccessor.java

@@ -0,0 +1,57 @@
+package org.springframework.expression;
+
+/**
+ * A CacheablePropertyAccessor is an optimized PropertyAccessor where the two parts of accessing the property are
+ * separated: (1) resolving the property and (2) retrieving its value. In some cases there is a large cost to
+ * discovering which property an expression refers to and once discovered it will always resolve to the same property.
+ * In these situations a CacheablePropertyAccessor enables the resolution to be done once and a reusable object (an
+ * executor) returned that can be called over and over to retrieve the property value without going through resolution
+ * again.
+ * <p>
+ * 
+ * @author Andy Clement
+ */
+public abstract class CacheablePropertyAccessor implements PropertyAccessor {
+
+	/**
+	 * Attempt to resolve the named property and return an executor that can be called to get the value of that
+	 * property. Return null if the property cannot be resolved.
+	 * 
+	 * @param context the evaluation context
+	 * @param target the target upon which the property is being accessed
+	 * @param name the name of the property being accessed
+	 * @return a reusable executor that can retrieve the property value
+	 */
+	public abstract PropertyReaderExecutor getReaderAccessor(EvaluationContext context, Object target, Object name);
+
+	/**
+	 * Attempt to resolve the named property and return an executor that can be called to set the value of that
+	 * property. Return null if the property cannot be resolved.
+	 * 
+	 * @param context the evaluation context
+	 * @param target the target upon which the property is being accessed
+	 * @param name the name of the property to be set
+	 * @return a reusable executor that can set the property value
+	 */
+	public abstract PropertyWriterExecutor getWriterAccessor(EvaluationContext context, Object target, Object name);
+
+	// Implementation of PropertyAccessor follows, based on the resolver/executor model
+
+	public final boolean canRead(EvaluationContext context, Object target, Object name) throws AccessException {
+		return getReaderAccessor(context, target, name) != null;
+	}
+
+	public final boolean canWrite(EvaluationContext context, Object target, Object name) throws AccessException {
+		return getWriterAccessor(context, target, name) != null;
+	}
+
+	public final Object read(EvaluationContext context, Object target, Object name) throws AccessException {
+		return getReaderAccessor(context, target, name).execute(context, target);
+	}
+
+	public final void write(EvaluationContext context, Object target, Object name, Object newValue)
+			throws AccessException {
+		getWriterAccessor(context, target, name).execute(context, target, newValue);
+	}
+
+}

+ 43 - 0
spring-el/src/main/java/org/springframework/expression/ConstructorExecutor.java

@@ -0,0 +1,43 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression;
+
+// TODO Is the resolver/executor model too pervasive in this package?
+/**
+ * Executors are built by resolvers and can be cached by the infrastructure to repeat an operation quickly without going
+ * back to the resolvers. For example, the particular constructor to run on a class may be discovered by the reflection
+ * constructor resolver - it will then build a ConstructorExecutor that executes that constructor and the
+ * ConstructorExecutor can be reused without needing to go back to the resolver to discover the constructor again.
+ * 
+ * They can become stale, and in that case should throw an AccessException - this will cause the infrastructure to go
+ * back to the resolvers to ask for a new one.
+ * 
+ * @author Andy Clement
+ */
+public interface ConstructorExecutor {
+
+	/**
+	 * Execute a constructor in the specified context using the specified arguments.
+	 * 
+	 * @param context the evaluation context in which the command is being executed
+	 * @param arguments the arguments to the constructor call, should match (in terms of number and type) whatever the
+	 * command will need to run
+	 * @return the new object
+	 * @throws AccessException if there is a problem executing the command or the CommandExecutor is no longer valid
+	 */
+	Object execute(EvaluationContext context, Object... arguments) throws AccessException;
+
+}

+ 39 - 0
spring-el/src/main/java/org/springframework/expression/ConstructorResolver.java

@@ -0,0 +1,39 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression;
+
+/**
+ * A constructor resolver attempts locate a constructor and returns a ConstructorExecutor that can be used to invoke
+ * that constructor. The ConstructorExecutor will be cached but if it 'goes stale' the resolvers will be called again.
+ * 
+ * @author Andy Clement
+ */
+public interface ConstructorResolver {
+
+	/**
+	 * Within the supplied context determine a suitable constructor on the supplied type that can handle the specified
+	 * arguments. Return a ConstructorExecutor that can be used to invoke that constructor (or null if no constructor
+	 * could be found).
+	 * 
+	 * @param context the current evaluation context
+	 * @param typename the type upon which to look for the constructor
+	 * @param argumentTypes the arguments that the constructor must be able to handle
+	 * @return a ConstructorExecutor that can invoke the constructor, or null if non found
+	 */
+	ConstructorExecutor resolve(EvaluationContext context, String typename, Class<?>[] argumentTypes)
+			throws AccessException;
+
+}

+ 87 - 0
spring-el/src/main/java/org/springframework/expression/EvaluationContext.java

@@ -0,0 +1,87 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression;
+
+import java.util.List;
+
+import org.springframework.expression.spel.standard.StandardEvaluationContext;
+import org.springframework.expression.spel.standard.StandardTypeUtilities;
+
+/**
+ * Expressions are executed in an evaluation context. It is in this context that references are resolved when
+ * encountered during expression evaluation.
+ * 
+ * There is a default implementation of the EvaluationContext, {@link StandardEvaluationContext} that can be extended,
+ * rather than having to implement everything.
+ * 
+ * @author Andy Clement
+ */
+public interface EvaluationContext {
+
+	/**
+	 * @return the root context object against which unqualified properties/methods/etc should be resolved
+	 */
+	Object getRootContextObject();
+
+	/**
+	 * @return a TypeUtilities implementation that can be used for looking up types, converting types, comparing types,
+	 * and overloading basic operators for types. A standard implementation is provided in {@link StandardTypeUtilities}
+	 */
+	TypeUtils getTypeUtils();
+
+	/**
+	 * Look up a named variable within this execution context.
+	 * 
+	 * @param name variable to lookup
+	 * @return the value of the variable
+	 */
+	Object lookupVariable(String name);
+
+	/**
+	 * Set a named variable within this execution context to a specified value.
+	 * 
+	 * @param name variable to set
+	 * @param value value to be placed in the variable
+	 */
+	void setVariable(String name, Object value);
+
+	// TODO lookupReference() - is it too expensive to return all objects within a context?
+	/**
+	 * Look up an object reference in a particular context. If no contextName is specified (null), assume the default
+	 * context. If no objectName is specified (null), return all objects in the specified context (List<Object>).
+	 * 
+	 * @param contextName the context in which to perform the lookup (or null for default context)
+	 * @param objectName the object to lookup in the context (or null to get all objects)
+	 * @return a specific object or List<Object>
+	 */
+	Object lookupReference(Object contextName, Object objectName) throws EvaluationException;
+
+	/**
+	 * @return a list of resolvers that will be asked in turn to locate a constructor
+	 */
+	List<ConstructorResolver> getConstructorResolvers();
+
+	/**
+	 * @return a list of resolvers that will be asked in turn to locate a method
+	 */
+	List<MethodResolver> getMethodResolvers();
+
+	/**
+	 * @return a list of accessors that will be asked in turn to read/write a property
+	 */
+	List<PropertyAccessor> getPropertyAccessors();
+
+}

+ 99 - 0
spring-el/src/main/java/org/springframework/expression/EvaluationException.java

@@ -0,0 +1,99 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ * 
+ * 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.springframework.expression;
+
+/**
+ * Base class for exceptions occurring during expression parsing and evaluation.
+ * 
+ * @author Andy Clement
+ */
+public class EvaluationException extends Exception {
+
+	/**
+	 * The expression string.
+	 */
+	private String expressionString;
+
+	/**
+	 * Creates a new expression exception. The expressionString field should be set by a later call to
+	 * setExpressionString().
+	 * 
+	 * @param cause the underlying cause of this exception
+	 */
+	public EvaluationException(Throwable cause) {
+		super(cause);
+	}
+
+	/**
+	 * Creates a new expression parsing exception.
+	 * 
+	 * @param expressionString the expression string that could not be parsed
+	 * @param cause the underlying cause of this exception
+	 */
+	public EvaluationException(String expressionString, Throwable cause) {
+		this(expressionString, "Exception occurred whilst handling '" + expressionString + "'", cause);
+	}
+
+	/**
+	 * Creates a new expression exception.
+	 * 
+	 * @param expressionString the expression string
+	 * @param message a descriptive message
+	 * @param cause the underlying cause of this exception
+	 */
+	public EvaluationException(String expressionString, String message, Throwable cause) {
+		super(message, cause);
+		this.expressionString = expressionString;
+	}
+
+	/**
+	 * Creates a new expression exception.
+	 * 
+	 * @param expressionString the expression string
+	 * @param message a descriptive message
+	 */
+	public EvaluationException(String expressionString, String message) {
+		super(message);
+		this.expressionString = expressionString;
+	}
+
+	/**
+	 * Creates a new expression exception. The expressionString field should be set by a later call to
+	 * setExpressionString().
+	 * 
+	 * @param message a descriptive message
+	 */
+	public EvaluationException(String message) {
+		super(message);
+	}
+
+	/**
+	 * Set the expression string, called on exceptions where the expressionString is not known at the time of exception
+	 * creation.
+	 * 
+	 * @param expressionString the expression string
+	 */
+	protected final void setExpressionString(String expressionString) {
+		this.expressionString = expressionString;
+	}
+
+	/**
+	 * @return the expression string
+	 */
+	public final String getExpressionString() {
+		return expressionString;
+	}
+}

+ 110 - 0
spring-el/src/main/java/org/springframework/expression/Expression.java

@@ -0,0 +1,110 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ * 
+ * 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.springframework.expression;
+
+/**
+ * An expression capable of evaluating itself against context objects. Encapsulates the details of a previously parsed
+ * expression string. Provides a common abstraction for expression evaluation independent of any language like OGNL or
+ * the Unified EL.
+ * 
+ * @author Keith Donald
+ * @author Andy Clement
+ */
+public interface Expression {
+
+	/**
+	 * Evaluate this expression in the default standard context.
+	 * 
+	 * @return the evaluation result
+	 * @throws EvaluationException if there is a problem during evaluation
+	 */
+	public Object getValue() throws EvaluationException;
+
+	/**
+	 * Evaluate the expression in the default standard context. If the result of the evaluation does not match (and
+	 * cannot be converted to) the expected result type then an exception will be returned.
+	 * 
+	 * @param desiredResultType the class the caller would like the result to be
+	 * @return the evaluation result
+	 * @throws EvaluationException if there is a problem during evaluation
+	 */
+	public Object getValue(Class<?> desiredResultType) throws EvaluationException;
+
+	/**
+	 * Evaluate this expression in the provided context and return the result of evaluation.
+	 * 
+	 * @param context the context in which to evaluate the expression
+	 * @return the evaluation result
+	 * @throws EvaluationException if there is a problem during evaluation
+	 */
+	public Object getValue(EvaluationContext context) throws EvaluationException;
+
+	/**
+	 * Evaluate the expression in a specified context which can resolve references to properties, methods, types, etc -
+	 * the type of the evaluation result is expected to be of a particular class and an exception will be thrown if it
+	 * is not and cannot be converted to that type.
+	 * 
+	 * @param context the context in which to evaluate the expression
+	 * @param desiredResultType the class the caller would like the result to be
+	 * @return the evaluation result
+	 * @throws EvaluationException if there is a problem during evaluation
+	 */
+	public Object getValue(EvaluationContext context, Class<?> desiredResultType) throws EvaluationException;
+
+	/**
+	 * Set this expression in the provided context to the value provided.
+	 * 
+	 * @param context the context in which to set the value of the expression
+	 * @param value the new value
+	 * @throws EvaluationException if there is a problem during evaluation
+	 */
+	public void setValue(EvaluationContext context, Object value) throws EvaluationException;
+
+	/**
+	 * Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)} method for
+	 * the given context.
+	 * 
+	 * @param context the context in which to evaluate the expression
+	 * @return the most general type of value that can be set on this context
+	 * @throws EvaluationException if there is a problem determining the type
+	 */
+	public Class getValueType(EvaluationContext context) throws EvaluationException;
+
+	/**
+	 * Returns the most general type that can be passed to the {@link #setValue(EvaluationContext, Object)} method using
+	 * the default context.
+	 * 
+	 * @return the most general type of value that can be set on this context
+	 * @throws EvaluationException if there is a problem determining the type
+	 */
+	public Class getValueType() throws EvaluationException;
+
+	/**
+	 * Returns the original string used to create this expression, unmodified.
+	 * 
+	 * @return the original expression string
+	 */
+	public String getExpressionString();
+
+	/**
+	 * Determine if an expression can be written to, i.e. setValue() can be called.
+	 * 
+	 * @param context the context in which the expression should be checked
+	 * @return true if the expression is writable
+	 * @throws EvaluationException if there is a problem determining if it is writable
+	 */
+	public boolean isWritable(EvaluationContext context) throws EvaluationException;
+}

+ 56 - 0
spring-el/src/main/java/org/springframework/expression/ExpressionParser.java

@@ -0,0 +1,56 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ * 
+ * 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.springframework.expression;
+
+/**
+ * Parses expression strings into compiled expressions that can be evaluated. Supports parsing templates as well as
+ * standard expression strings.
+ * 
+ * @author Keith Donald
+ * @author Andy Clement
+ */
+public interface ExpressionParser {
+
+	/**
+	 * Parse the expression string and return an Expression object you can use for repeated evaluation. Some examples:
+	 * 
+	 * <pre>
+	 *     3 + 4
+	 *     name.firstName
+	 * </pre>
+	 * 
+	 * @param expressionString the raw expression string to parse
+	 * @param context a context for influencing this expression parsing routine (optional)
+	 * @return an evaluator for the parsed expression
+	 * @throws ParseException an exception occurred during parsing
+	 */
+	public Expression parseExpression(String expressionString, ParserContext context) throws ParseException;
+
+	/**
+	 * Parse the expression string and return an Expression object you can use for repeated evaluation. Some examples:
+	 * 
+	 * <pre>
+	 *     3 + 4
+	 *     name.firstName
+	 * </pre>
+	 * 
+	 * @param expressionString the raw expression string to parse
+	 * @return an evaluator for the parsed expression
+	 * @throws ParseException an exception occurred during parsing
+	 */
+	public Expression parseExpression(String expressionString) throws ParseException;
+
+}

+ 43 - 0
spring-el/src/main/java/org/springframework/expression/MethodExecutor.java

@@ -0,0 +1,43 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression;
+
+/**
+ * MethodExecutors are built by the resolvers and can be cached by the infrastructure to repeat an operation quickly
+ * without going back to the resolvers. For example, the particular method to run on an object may be discovered by the
+ * reflection method resolver - it will then build a MethodExecutor that executes that method and the MethodExecutor can
+ * be reused without needing to go back to the resolver to discover the method again.
+ * <p>
+ * They can become stale, and in that case should throw an AccessException - this will cause the infrastructure to go
+ * back to the resolvers to ask for a new one.
+ * 
+ * @author Andy Clement
+ */
+public interface MethodExecutor {
+
+	/**
+	 * Execute a command using the specified arguments, and using the specified expression state.
+	 * 
+	 * @param context the evaluation context in which the command is being executed
+	 * @param target the target object of the call - null for static methods
+	 * @param methodArguments the arguments to the executor, should match (in terms of number and type) whatever the
+	 * command will need to run
+	 * @return the value returned from execution
+	 * @throws AccessException if there is a problem executing the command or the MethodExecutor is no longer valid
+	 */
+	Object execute(EvaluationContext context, Object target, Object... methodArguments) throws AccessException;
+
+}

+ 38 - 0
spring-el/src/main/java/org/springframework/expression/MethodResolver.java

@@ -0,0 +1,38 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression;
+
+/**
+ * A method resolver attempts locate a method and returns a command executor that can be used to invoke that method. The
+ * command executor will be cached but if it 'goes stale' the resolvers will be called again.
+ * 
+ * @author Andy Clement
+ */
+public interface MethodResolver {
+
+	/**
+	 * Within the supplied context determine a suitable method on the supplied object that can handle the specified
+	 * arguments. Return a MethodExecutor that can be used to invoke that method (or null if no method
+	 * could be found).
+	 * 
+	 * @param context the current evaluation context
+	 * @param targetObject the object upon which the method is being called
+	 * @param argumentTypes the arguments that the constructor must be able to handle
+	 * @return a MethodExecutor that can invoke the method, or null if the method cannot be found
+	 */
+	MethodExecutor resolve(EvaluationContext context, Object targetObject, String name, Class<?>[] argumentTypes) throws AccessException;
+
+}

+ 26 - 0
spring-el/src/main/java/org/springframework/expression/Operation.java

@@ -0,0 +1,26 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression;
+
+
+/**
+ * Supported operations that an {@link OperatorOverloader} can implement for any pair of operands.
+ * 
+ * @author Andy Clement
+ */
+public enum Operation {
+	ADD, SUBTRACT, DIVIDE, MULTIPLY, MODULUS;
+}

+ 37 - 0
spring-el/src/main/java/org/springframework/expression/OperatorOverloader.java

@@ -0,0 +1,37 @@
+package org.springframework.expression;
+
+/**
+ * By default the mathematical operators {@link Operation} support simple types like numbers. By providing an
+ * implementation of OperatorOverloader, a user of the expression language can support these operations on other types.
+ * 
+ * @author Andy Clement
+ */
+public interface OperatorOverloader {
+
+	// TODO does type OperatorOverloader need a better name?
+	// TODO Operator overloading needs some testing!
+
+	/**
+	 * Return true if the operator overloader supports the specified operation between the two operands and so should be
+	 * invoked to handle it.
+	 * 
+	 * @param operation the operation to be performed
+	 * @param leftOperand the left operand
+	 * @param rightOperand the right operand
+	 * @return true if the OperatorOverloader supports the specified operation between the two operands
+	 * @throws EvaluationException if there is a problem performing the operation
+	 */
+	boolean overridesOperation(Operation operation, Object leftOperand, Object rightOperand) throws EvaluationException;
+
+	/**
+	 * Execute the specified operation on two operands, returning a result. See {@link Operation} for supported
+	 * operations.
+	 * 
+	 * @param operation the operation to be performed
+	 * @param leftOperand the left operand
+	 * @param rightOperand the right operand
+	 * @return the result of performing the operation on the two operands
+	 * @throws EvaluationException if there is a problem performing the operation
+	 */
+	Object operate(Operation operation, Object leftOperand, Object rightOperand) throws EvaluationException;
+}

+ 89 - 0
spring-el/src/main/java/org/springframework/expression/ParseException.java

@@ -0,0 +1,89 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ * 
+ * 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.springframework.expression;
+
+/**
+ * Base class for exceptions occurring during expression parsing and evaluation.
+ * 
+ * @author Andy Clement
+ */
+public class ParseException extends Exception {
+
+	/**
+	 * The expression string.
+	 */
+	private String expressionString;
+
+	/**
+	 * Creates a new expression exception. The expressionString field should be set by a later call to
+	 * setExpressionString().
+	 * 
+	 * @param cause the underlying cause of this exception
+	 */
+	public ParseException(Throwable cause) {
+		super(cause);
+	}
+
+	/**
+	 * Creates a new expression parsing exception.
+	 * 
+	 * @param expressionString the expression string that could not be parsed
+	 * @param cause the underlying cause of this exception
+	 */
+	public ParseException(String expressionString, Throwable cause) {
+		this(expressionString, "Exception occurred whilst handling '" + expressionString + "'", cause);
+	}
+
+	/**
+	 * Creates a new expression exception.
+	 * 
+	 * @param expressionString the expression string
+	 * @param message a descriptive message
+	 * @param cause the underlying cause of this exception
+	 */
+	public ParseException(String expressionString, String message, Throwable cause) {
+		super(message, cause);
+		this.expressionString = expressionString;
+	}
+
+	/**
+	 * Creates a new expression exception.
+	 * 
+	 * @param expressionString the expression string
+	 * @param message a descriptive message
+	 */
+	public ParseException(String expressionString, String message) {
+		super(message);
+		this.expressionString = expressionString;
+	}
+
+	/**
+	 * Set the expression string, called on exceptions where the expressionString is not known at the time of exception
+	 * creation.
+	 * 
+	 * @param expressionString the expression string
+	 */
+	protected final void setExpressionString(String expressionString) {
+		this.expressionString = expressionString;
+	}
+
+	/**
+	 * @return the expression string
+	 */
+	public final String getExpressionString() {
+		return expressionString;
+	}
+}

+ 55 - 0
spring-el/src/main/java/org/springframework/expression/ParserContext.java

@@ -0,0 +1,55 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression;
+
+/**
+ * Input provided to an expression parser that can influence an expression parsing/compilation routine.
+ * 
+ * @author Keith Donald
+ * @author Andy Clement
+ */
+public interface ParserContext {
+
+	/**
+	 * Whether or not the expression being parsed is a template. A template expression consists of literal text that can
+	 * be mixed with evaluatable blocks. Some examples:
+	 * 
+	 * <pre>
+	 * 	   Some literal text
+	 *     Hello ${name.firstName}!
+	 *     ${3 + 4}
+	 * </pre>
+	 * 
+	 * @return true if the expression is a template, false otherwise
+	 */
+	public boolean isTemplate();
+
+	/**
+	 * For template expressions, returns the prefix that identifies the start of an expression block within a string.
+	 * For example "${"
+	 * 
+	 * @return the prefix that identifies the start of an expression
+	 */
+	public String getExpressionPrefix();
+
+	/**
+	 * For template expressions, return the prefix that identifies the end of an expression block within a string. For
+	 * example "}$"
+	 * 
+	 * @return the suffix that identifies the end of an expression
+	 */
+	public String getExpressionSuffix();
+}

+ 88 - 0
spring-el/src/main/java/org/springframework/expression/PropertyAccessor.java

@@ -0,0 +1,88 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression;
+
+/**
+ * A property accessor is able to read (and possibly write) to object properties. The interface places no restrictions
+ * and so implementors are free to access properties directly as fields or through getters or in any other way they see
+ * as appropriate. A resolver can optionally specify an array of target classes for which it should be called - but if
+ * it returns null from getSpecificTargetClasses() then it will be called for all property references and given a chance
+ * to determine if it can read or write them. Property resolvers are considered to be ordered and each will be called in
+ * turn. The only rule that affects the call order is that any naming the target class directly in
+ * getSpecifiedTargetClasses() will be called first, before the general resolvers.
+ * <p>
+ * If the cost of locating the property is expensive, in relation to actually retrieving its value, consider extending
+ * CacheablePropertyAccessor rather than directly implementing PropertyAccessor. A CacheablePropertyAccessor enables the
+ * discovery (resolution) of the property to be done once and then an object (an executor) returned and cached by the
+ * infrastructure that can be used repeatedly to retrieve the property value.
+ * 
+ * @author Andy Clement
+ */
+public interface PropertyAccessor {
+
+	/**
+	 * Return an array of classes for which this resolver should be called. Returning null indicates this is a general
+	 * resolver that can be called in an attempt to resolve a property on any type.
+	 * 
+	 * @return an array of classes that this resolver is suitable for (or null if a general resolver)
+	 */
+	public Class[] getSpecificTargetClasses();
+
+	/**
+	 * Called to determine if a resolver instance is able to access a specified property on a specified target object.
+	 * 
+	 * @param context the evaluation context in which the access is being attempted
+	 * @param target the target object upon which the property is being accessed
+	 * @param name the name of the property being accessed
+	 * @return true if this resolver is able to read the property
+	 * @throws AccessException if there is any problem determining whether the property can be read
+	 */
+	public boolean canRead(EvaluationContext context, Object target, Object name) throws AccessException;
+
+	/**
+	 * Called to read a property from a specified target object
+	 * 
+	 * @param context the evaluation context in which the access is being attempted
+	 * @param target the target object upon which the property is being accessed
+	 * @param name the name of the property being accessed
+	 * @return Object the value of the property
+	 * @throws AccessException if there is any problem accessing the property value
+	 */
+	public Object read(EvaluationContext context, Object target, Object name) throws AccessException;
+
+	/**
+	 * Called to determine if a resolver instance is able to write to a specified property on a specified target object.
+	 * 
+	 * @param context the evaluation context in which the access is being attempted
+	 * @param target the target object upon which the property is being accessed
+	 * @param name the name of the property being accessed
+	 * @return true if this resolver is able to write to the property
+	 * @throws AccessException if there is any problem determining whether the property can be written to
+	 */
+	public boolean canWrite(EvaluationContext context, Object target, Object name) throws AccessException;
+
+	/**
+	 * Called to write to a property on a specified target object. Should only succeed if canWrite() also returns true.
+	 * 
+	 * @param context the evaluation context in which the access is being attempted
+	 * @param target the target object upon which the property is being accessed
+	 * @param name the name of the property being accessed
+	 * @param newValue the new value for the property
+	 * @throws AccessException if there is any problem writing to the property value
+	 */
+	public void write(EvaluationContext context, Object target, Object name, Object newValue) throws AccessException;
+
+}

+ 40 - 0
spring-el/src/main/java/org/springframework/expression/PropertyReaderExecutor.java

@@ -0,0 +1,40 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression;
+
+/**
+ * If a property accessor is built upon the CacheablePropertyAccessor class then once the property
+ * has been resolved the accessor will return an instance of this PropertyReaderExecutor interface
+ * that can be cached and repeatedly called to access the value of the property. 
+ * <p>
+ * They can become stale, and in that case should throw an AccessException - this will cause the 
+ * infrastructure to go back to the resolvers to ask for a new one.
+ *
+ * @author Andy Clement
+ */
+public interface PropertyReaderExecutor {
+
+	/**
+	 * Return the value of a property for the specified target.
+	 *
+	 * @param context the evaluation context in which the command is being executed
+	 * @param targetObject the target object on which property access is being attempted
+	 * @return the property value
+	 * @throws AccessException if there is a problem accessing the property or this executor has become stale
+	 */
+	Object execute(EvaluationContext context, Object targetObject) throws AccessException;
+
+}

+ 40 - 0
spring-el/src/main/java/org/springframework/expression/PropertyWriterExecutor.java

@@ -0,0 +1,40 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression;
+
+/**
+ * If a property accessor is built upon the CacheablePropertyAccessor class then once the property
+ * has been resolved the accessor will return an instance of this PropertyWriterExecutor interface
+ * that can be cached and repeatedly called to set the value of the property. 
+ * <p>
+ * They can become stale, and in that case should throw an AccessException - this will cause the 
+ * infrastructure to go back to the resolvers to ask for a new one.
+ *
+ * @author Andy Clement
+ */
+public interface PropertyWriterExecutor {
+
+	/**
+	 * Set the value of a property to the supplied new value.
+	 *
+	 * @param context the evaluation context in which the command is being executed
+	 * @param targetObject the target object on which property write is being attempted
+	 * @param newValue the new value for the property
+	 * @throws AccessException if there is a problem setting the property or this executor has become stale
+	 */
+	void execute(EvaluationContext evaluationContext, Object targetObject, Object newValue) throws AccessException;
+
+}

+ 46 - 0
spring-el/src/main/java/org/springframework/expression/TypeComparator.java

@@ -0,0 +1,46 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression;
+
+/**
+ * Instances of a type comparator should be able to compare pairs of objects for equality, the specification of the
+ * return value is the same as for {@link Comparable}.
+ * 
+ * @author Andy Clement
+ */
+public interface TypeComparator {
+
+	/**
+	 * Compare two objects.
+	 * 
+	 * @param firstObject the first object
+	 * @param secondObject the second object
+	 * @return 0 if they are equal, <0 if the first is smaller than the second, or >0 if the first is larger than the
+	 * second
+	 * @throws EvaluationException if a problem occurs during comparison (or they are not comparable)
+	 */
+	int compare(Object firstObject, Object secondObject) throws EvaluationException;
+
+	/**
+	 * Return true if the comparator can compare these two objects
+	 * 
+	 * @param firstObject the first object
+	 * @param secondObject the second object
+	 * @return true if the comparator can compare these objects
+	 */
+	public boolean canCompare(Object firstObject, Object secondObject);
+
+}

+ 52 - 0
spring-el/src/main/java/org/springframework/expression/TypeConverter.java

@@ -0,0 +1,52 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression;
+
+import org.springframework.expression.spel.standard.StandardIndividualTypeConverter;
+import org.springframework.expression.spel.standard.StandardTypeConverter;
+
+/**
+ * A type converter can convert values between different types. There is a default implementation called
+ * {@link StandardTypeConverter} that supports some basic conversions. That default implementation can be extended
+ * through subclassing or via registration of new {@link StandardIndividualTypeConverter} instances with the
+ * StandardTypeConverter.
+ * 
+ * @author Andy Clement
+ */
+public interface TypeConverter {
+	// TODO replace this stuff with Keiths spring-binding conversion code
+	// TODO should ExpressionException be thrown for lost precision in the case of coercion?
+
+	/**
+	 * Convert (may coerce) a value from one type to another, for example from a boolean to a string.
+	 * 
+	 * @param value the value to be converted
+	 * @param targetType the type that the value should be converted to if possible
+	 * @return the converted value
+	 * @throws EvaluationException if conversion is not possible
+	 */
+	Object convertValue(Object value, Class<?> targetType) throws EvaluationException;
+
+	/**
+	 * Return true if the type converter can convert the specified type to the desired target type.
+	 * 
+	 * @param sourceType the type to be converted from
+	 * @param targetType the type to be converted to
+	 * @return true if that conversion can be performed
+	 */
+	public boolean canConvert(Class<?> sourceType, Class<?> targetType);
+
+}

+ 38 - 0
spring-el/src/main/java/org/springframework/expression/TypeLocator.java

@@ -0,0 +1,38 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression;
+
+import org.springframework.expression.spel.standard.StandardTypeLocator;
+
+/**
+ * Implementors of this interface are expected to be able to locate types. They may use custom classloaders or the
+ * and deal with common package prefixes (java.lang, etc) however they wish. See
+ * {@link StandardTypeLocator} for an example implementation.
+ * 
+ * @author Andy Clement
+ */
+public interface TypeLocator {
+
+	/**
+	 * Find a type by name. The name may or may not be fully qualified (eg. String or java.lang.String)
+	 * 
+	 * @param type the type to be located
+	 * @return the class object representing that type
+	 * @throw ExpressionException if there is a problem finding it
+	 */
+	Class<?> findType(String typename) throws EvaluationException;
+
+}

+ 54 - 0
spring-el/src/main/java/org/springframework/expression/TypeUtils.java

@@ -0,0 +1,54 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression;
+
+/**
+ * TypeUtilities brings together the various kinds of type related function that may occur
+ * whilst working with expressions.  An implementor is providing support for four type related
+ * facilities:
+ * <ul>
+ * <li>a mechanism for finding types
+ * <li>a mechanism for comparing types
+ * <li>a mechanism for type conversion/coercion
+ * <li>a mechanism for overloading mathematical operations (add/subtract/etc)
+ * </ul>
+ * 
+ * @author Andy Clement
+ */
+public interface TypeUtils {
+
+	/**
+	 * @return a type locator that can be used to find types, either by short or fully qualified name.
+	 */
+	TypeLocator getTypeLocator();
+
+	/**
+	 * @return a type comparator for comparing pairs of objects for equality.
+	 */
+	TypeComparator getTypeComparator();
+
+	/**
+	 * @return a type converter that can convert (or coerce) a value from one type to another.
+	 */
+	TypeConverter getTypeConverter();
+
+	/**
+	 * @return an operator overloader that may support mathematical operations between more than the standard set of
+	 * types
+	 */
+	OperatorOverloader getOperatorOverloader();
+
+}

+ 78 - 0
spring-el/src/main/java/org/springframework/expression/common/CompositeStringExpression.java

@@ -0,0 +1,78 @@
+package org.springframework.expression.common;
+
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.Expression;
+
+/**
+ * Represents a template expression broken into pieces. Each piece will be an Expression but pure text parts to the
+ * template will be represented as LiteralExpression objects. An example of a template expression might be: <code><pre>
+ * &quot;Hello ${getName()}&quot;
+ * </pre></code> which will be represented as a CompositeStringExpression of two parts. The first part being a
+ * LiteralExpression representing 'Hello ' and the second part being a real expression that will call getName() when
+ * invoked.
+ * 
+ * @author Andy Clement
+ */
+public class CompositeStringExpression implements Expression {
+
+	private final String expressionString;
+
+	/**
+	 * The array of expressions that make up the composite expression
+	 */
+	private final Expression[] expressions;
+
+	public CompositeStringExpression(String expressionString, Expression[] expressions) {
+		this.expressionString = expressionString;
+		this.expressions = expressions;
+	}
+
+	public String getExpressionString() {
+		return expressionString;
+	}
+
+	public String getValue() throws EvaluationException {
+		StringBuilder sb = new StringBuilder();
+		for (int i = 0; i < expressions.length; i++) {
+			// TODO is stringify ok for the non literal components? or should the converters be used? see another
+			// case below
+			sb.append(expressions[i].getValue());
+		}
+		return sb.toString();
+	}
+
+	public String getValue(EvaluationContext context) throws EvaluationException {
+		StringBuilder sb = new StringBuilder();
+		for (int i = 0; i < expressions.length; i++) {
+			sb.append(expressions[i].getValue(context));
+		}
+		return sb.toString();
+	}
+
+	public Class getValueType(EvaluationContext context) throws EvaluationException {
+		return String.class;
+	}
+
+	public Class getValueType() throws EvaluationException {
+		return String.class;
+	}
+
+	public void setValue(EvaluationContext context, Object value) throws EvaluationException {
+		throw new EvaluationException(expressionString, "Cannot call setValue() on a composite expression");
+	}
+
+	public Object getValue(EvaluationContext context, Class<?> expectedResultType) throws EvaluationException {
+		Object value = getValue(context);
+		return ExpressionUtils.convert(context, value, expectedResultType);
+	}
+
+	public Object getValue(Class<?> expectedResultType) throws EvaluationException {
+		Object value = getValue();
+		return ExpressionUtils.convert(null, value, expectedResultType);
+	}
+
+	public boolean isWritable(EvaluationContext context) throws EvaluationException {
+		return false;
+	}
+}

+ 24 - 0
spring-el/src/main/java/org/springframework/expression/common/DefaultNonTemplateParserContext.java

@@ -0,0 +1,24 @@
+package org.springframework.expression.common;
+
+import org.springframework.expression.ParserContext;
+
+public class DefaultNonTemplateParserContext implements ParserContext {
+
+	public static final DefaultNonTemplateParserContext INSTANCE = new DefaultNonTemplateParserContext();
+
+	private DefaultNonTemplateParserContext() {
+	}
+
+	public String getExpressionPrefix() {
+		return null;
+	}
+
+	public String getExpressionSuffix() {
+		return null;
+	}
+
+	public boolean isTemplate() {
+		return false;
+	}
+
+}

+ 24 - 0
spring-el/src/main/java/org/springframework/expression/common/DefaultTemplateParserContext.java

@@ -0,0 +1,24 @@
+package org.springframework.expression.common;
+
+import org.springframework.expression.ParserContext;
+
+public class DefaultTemplateParserContext implements ParserContext {
+
+	public static final DefaultTemplateParserContext INSTANCE = new DefaultTemplateParserContext();
+
+	private DefaultTemplateParserContext() {
+	}
+
+	public String getExpressionPrefix() {
+		return "${";
+	}
+
+	public String getExpressionSuffix() {
+		return "}";
+	}
+
+	public boolean isTemplate() {
+		return true;
+	}
+
+}

+ 55 - 0
spring-el/src/main/java/org/springframework/expression/common/ExpressionUtils.java

@@ -0,0 +1,55 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.common;
+
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.TypeConverter;
+import org.springframework.expression.TypeUtils;
+
+/**
+ * Common utility functions that may be used by any Expression Language provider.
+ * 
+ * @author Andy Clement
+ */
+public class ExpressionUtils {
+
+	/**
+	 * Determines if there is a type converter available in the specified context and attempts to use it to convert the
+	 * supplied value to the specified type. Throws an exception if conversion is not possible.
+	 * 
+	 * @param context the evaluation context that may define a type converter
+	 * @param value the value to convert (may be null)
+	 * @param toType the type to attempt conversion to
+	 * @return the converted value
+	 * @throws EvaluationException if there is a problem during conversion or conversion of the value to the specified
+	 * type is not supported
+	 */
+	public static Object convert(EvaluationContext context, Object value, Class<?> toType) throws EvaluationException {
+		if (value == null || toType == null || toType.isAssignableFrom(value.getClass())) {
+			return value;
+		}
+		if (context != null) {
+			TypeUtils typeUtils = context.getTypeUtils();
+			if (typeUtils != null) {
+				TypeConverter typeConverter = typeUtils.getTypeConverter();
+				return typeConverter.convertValue(value, toType);
+			}
+		}
+		throw new EvaluationException("Cannot convert value '" + value + "' to type '" + toType.getName() + "'");
+	}
+
+}

+ 65 - 0
spring-el/src/main/java/org/springframework/expression/common/LiteralExpression.java

@@ -0,0 +1,65 @@
+package org.springframework.expression.common;
+
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.Expression;
+
+/**
+ * A very simple hardcoded implementation of the Expression interface that represents a string literal. It is used with
+ * CompositeStringExpression when representing a template expression which is made up of pieces - some being real
+ * expressions to be handled by an EL implementation like Spel, and some being just textual elements.
+ * 
+ * @author Andy Clement
+ * 
+ */
+public class LiteralExpression implements Expression {
+
+	/**
+	 * Fixed literal value of this expression
+	 */
+	private final String literalValue;
+
+	public LiteralExpression(String literalValue) {
+		this.literalValue = literalValue;
+	}
+
+	public String getExpressionString() {
+		return literalValue;
+		// return new StringBuilder().append("'").append(literalValue).append("'").toString();
+	}
+
+	public String getValue() throws EvaluationException {
+		return literalValue;
+	}
+
+	public String getValue(EvaluationContext context) throws EvaluationException {
+		return literalValue;
+	}
+
+	public Class getValueType(EvaluationContext context) throws EvaluationException {
+		return String.class;
+	}
+
+	public void setValue(EvaluationContext context, Object value) throws EvaluationException {
+		throw new EvaluationException(literalValue, "Cannot call setValue() on a LiteralExpression");
+	}
+
+	public Object getValue(EvaluationContext context, Class<?> expectedResultType) throws EvaluationException {
+		Object value = getValue(context);
+		return ExpressionUtils.convert(context, value, expectedResultType);
+	}
+
+	public Object getValue(Class<?> expectedResultType) throws EvaluationException {
+		Object value = getValue();
+		return ExpressionUtils.convert(null, value, expectedResultType);
+	}
+
+	public boolean isWritable(EvaluationContext context) throws EvaluationException {
+		return false;
+	}
+
+	public Class getValueType() throws EvaluationException {
+		return String.class;
+	}
+
+}

+ 115 - 0
spring-el/src/main/java/org/springframework/expression/common/TemplateAwareExpressionParser.java

@@ -0,0 +1,115 @@
+package org.springframework.expression.common;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.springframework.expression.Expression;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.ParseException;
+import org.springframework.expression.ParserContext;
+
+/**
+ * An expression parser that understands templates. It can be subclassed by expression parsers that do not offer first
+ * class support for templating.
+ * 
+ * @author Keith Donald
+ * @author Andy Clement
+ */
+public abstract class TemplateAwareExpressionParser implements ExpressionParser {
+
+	public Expression parseExpression(String expressionString) throws ParseException {
+		return parseExpression(expressionString, DefaultNonTemplateParserContext.INSTANCE);
+	}
+
+	public final Expression parseExpression(String expressionString, ParserContext context) throws ParseException {
+		if (context == null) {
+			context = DefaultNonTemplateParserContext.INSTANCE;
+		}
+		if (context.isTemplate()) {
+			return parseTemplate(expressionString, context);
+		} else {
+			return doParseExpression(expressionString, context);
+		}
+	}
+
+	private Expression parseTemplate(String expressionString, ParserContext context) throws ParseException {
+		if (expressionString.length() == 0) {
+			// TODO throw exception if there are template prefix/suffixes and it is length 0?
+			return new LiteralExpression("");
+		}
+		Expression[] expressions = parseExpressions(expressionString, context);
+		if (expressions.length == 1) {
+			return expressions[0];
+		} else {
+			return new CompositeStringExpression(expressionString, expressions);
+		}
+	}
+
+	// helper methods
+
+	/**
+	 * Helper that parses given expression string using the configured parser. The expression string can contain any
+	 * number of expressions all contained in "${...}" markers. For instance: "foo${expr0}bar${expr1}". The static
+	 * pieces of text will also be returned as Expressions that just return that static piece of text. As a result,
+	 * evaluating all returned expressions and concatenating the results produces the complete evaluated string.
+	 * Unwrapping is only done of the outermost delimiters found, so the string 'hello ${foo${abc}}' would break into
+	 * the pieces 'hello ' and 'foo${abc}'. This means that expression languages that used ${..} as part of their
+	 * functionality are supported without any problem
+	 * 
+	 * @param expressionString the expression string
+	 * @return the parsed expressions
+	 * @throws ParseException when the expressions cannot be parsed
+	 */
+	private final Expression[] parseExpressions(String expressionString, ParserContext context) throws ParseException {
+		// TODO this needs to handle nested delimiters for cases where the expression uses the delim chars
+		List<Expression> expressions = new LinkedList<Expression>();
+		int startIdx = 0;
+		String prefix = context.getExpressionPrefix();
+		String suffix = context.getExpressionSuffix();
+		while (startIdx < expressionString.length()) {
+			int prefixIndex = expressionString.indexOf(prefix, startIdx);
+			if (prefixIndex >= startIdx) {
+				// a inner expression was found - this is a composite
+				if (prefixIndex > startIdx) {
+					expressions.add(new LiteralExpression(expressionString.substring(startIdx, prefixIndex)));
+					startIdx = prefixIndex;
+				}
+				int nextPrefixIndex = expressionString.indexOf(prefix, prefixIndex + prefix.length());
+				int suffixIndex;
+				if (nextPrefixIndex == -1) {
+					// this is the last expression in the expression string
+					suffixIndex = expressionString.lastIndexOf(suffix);
+
+				} else {
+					// another expression exists after this one in the expression string
+					suffixIndex = expressionString.lastIndexOf(suffix, nextPrefixIndex);
+				}
+				if (suffixIndex < (prefixIndex + prefix.length())) {
+					throw new ParseException(expressionString, "No ending suffix '" + suffix
+							+ "' for expression starting at character " + prefixIndex + ": "
+							+ expressionString.substring(prefixIndex), null);
+				} else if (suffixIndex == prefixIndex + prefix.length()) {
+					throw new ParseException(expressionString, "No expression defined within delimiter '" + prefix
+							+ suffix + "' at character " + prefixIndex, null);
+				} else {
+					String expr = expressionString.substring(prefixIndex + prefix.length(), suffixIndex);
+					expressions.add(doParseExpression(expr, context));
+					startIdx = suffixIndex + suffix.length();
+				}
+			} else {
+				if (startIdx == 0) {
+					expressions.add(doParseExpression(expressionString, context));
+				} else {
+					// no more ${expressions} found in string, add rest as static text
+					expressions.add(new LiteralExpression(expressionString.substring(startIdx)));
+				}
+				startIdx = expressionString.length();
+			}
+		}
+		return expressions.toArray(new Expression[expressions.size()]);
+	}
+
+	protected abstract Expression doParseExpression(String expressionString, ParserContext context)
+			throws ParseException;
+
+}

+ 202 - 0
spring-el/src/main/java/org/springframework/expression/spel/ExpressionState.java

@@ -0,0 +1,202 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.Operation;
+import org.springframework.expression.OperatorOverloader;
+import org.springframework.expression.PropertyAccessor;
+import org.springframework.expression.TypeComparator;
+import org.springframework.expression.TypeConverter;
+import org.springframework.expression.TypeUtils;
+import org.springframework.expression.spel.internal.VariableScope;
+
+/**
+ * An ExpressionState is for maintaining per-expression-evaluation state, any changes to it are not seen by other
+ * expressions but it gives a place to hold local variables and for component expressions in a compound expression to
+ * communicate state. This is in contrast to the EvaluationContext, which is shared amongst expression evaluations, and
+ * any changes to it will be seen by other expressions or any code that chooses to ask questions of the context.
+ * 
+ * It also acts as a place for to define common utility routines that the various Ast nodes might need.
+ * 
+ * @author Andy Clement
+ */
+public class ExpressionState {
+
+	private EvaluationContext relatedContext;
+
+	private final Stack<VariableScope> variableScopes = new Stack<VariableScope>();
+
+	private final Stack<Object> contextObjects = new Stack<Object>();
+
+	public ExpressionState(EvaluationContext context) {
+		relatedContext = context;
+		createVariableScope();
+	}
+
+	public ExpressionState() {
+		createVariableScope();
+	}
+
+	private void createVariableScope() {
+		variableScopes.add(new VariableScope()); // create an empty top level VariableScope
+	}
+
+	/**
+	 * The active context object is what unqualified references to properties/etc are resolved against.
+	 */
+	public Object getActiveContextObject() {
+		if (contextObjects.isEmpty()) {
+			return relatedContext.getRootContextObject();
+		}
+		return contextObjects.peek();
+	}
+
+	public void pushActiveContextObject(Object obj) {
+		contextObjects.push(obj);
+	}
+
+	public void popActiveContextObject() {
+		contextObjects.pop();
+	}
+
+	public Object getRootContextObject() {
+		return relatedContext.getRootContextObject();
+	}
+
+	public Object lookupReference(Object contextName, Object objectName) throws EvaluationException {
+		return relatedContext.lookupReference(contextName, objectName);
+	}
+
+	public TypeUtils getTypeUtilities() {
+		return relatedContext.getTypeUtils();
+	}
+
+	public TypeComparator getTypeComparator() {
+		return relatedContext.getTypeUtils().getTypeComparator();
+	}
+
+	public Class<?> findType(String type) throws EvaluationException {
+		return getTypeUtilities().getTypeLocator().findType(type);
+	}
+
+	// TODO all these methods that grab the type converter will fail badly if there isn't one...
+	public boolean toBoolean(Object value) throws EvaluationException {
+		return ((Boolean) getTypeConverter().convertValue(value, Boolean.TYPE)).booleanValue();
+	}
+
+	public char toCharacter(Object value) throws EvaluationException {
+		return ((Character) getTypeConverter().convertValue(value, Character.TYPE)).charValue();
+	}
+
+	public short toShort(Object value) throws EvaluationException {
+		return ((Short) getTypeConverter().convertValue(value, Short.TYPE)).shortValue();
+	}
+
+	public int toInteger(Object value) throws EvaluationException {
+		return ((Integer) getTypeConverter().convertValue(value, Integer.TYPE)).intValue();
+	}
+
+	public double toDouble(Object value) throws EvaluationException {
+		return ((Double) getTypeConverter().convertValue(value, Double.TYPE)).doubleValue();
+	}
+
+	public float toFloat(Object value) throws EvaluationException {
+		return ((Float) getTypeConverter().convertValue(value, Float.TYPE)).floatValue();
+	}
+
+	public long toLong(Object value) throws EvaluationException {
+		return ((Long) getTypeConverter().convertValue(value, Long.TYPE)).longValue();
+	}
+
+	public byte toByte(Object value) throws EvaluationException {
+		return ((Byte) getTypeConverter().convertValue(value, Byte.TYPE)).byteValue();
+	}
+
+	public TypeConverter getTypeConverter() {
+		// TODO cache TypeConverter when it is set/changed?
+		return getTypeUtilities().getTypeConverter();
+	}
+
+	public void setVariable(String name, Object value) {
+		relatedContext.setVariable(name, value);
+	}
+
+	public Object lookupVariable(String name) {
+		return relatedContext.lookupVariable(name);
+	}
+
+	/**
+	 * A new scope is entered when a function is invoked
+	 */
+	public void enterScope(Map<String, Object> argMap) {
+		variableScopes.push(new VariableScope(argMap));
+	}
+
+	public void enterScope(String name, Object value) {
+		variableScopes.push(new VariableScope(name, value));
+	}
+
+	public void exitScope() {
+		variableScopes.pop();
+	}
+
+	public void setLocalVariable(String name, Object value) {
+		variableScopes.peek().setVariable(name, value);
+	}
+
+	public Object lookupLocalVariable(String name) {
+		int scopeNumber = variableScopes.size() - 1;
+		for (int i = scopeNumber; i >= 0; i--) {
+			if (variableScopes.get(i).definesVariable(name)) {
+				return variableScopes.get(i).lookupVariable(name);
+			}
+		}
+		return null;
+	}
+
+	public Object operate(Operation op, Object left, Object right) throws SpelException {
+		OperatorOverloader overloader = relatedContext.getTypeUtils().getOperatorOverloader();
+		try {
+			if (overloader != null && overloader.overridesOperation(op, left, right)) {
+				return overloader.operate(op, left, right);
+			} else {
+				throw new SpelException(SpelMessages.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES, op, left, right);
+			}
+		} catch (EvaluationException e) {
+			if (e instanceof SpelException) {
+				throw (SpelException) e;
+			} else {
+				throw new SpelException(e, SpelMessages.UNEXPECTED_PROBLEM_INVOKING_OPERATOR, op, left, right, e
+						.getMessage());
+			}
+		}
+	}
+
+	public List<PropertyAccessor> getPropertyAccessors() {
+		return relatedContext.getPropertyAccessors();
+	}
+
+	public EvaluationContext getEvaluationContext() {
+		return relatedContext;
+	}
+
+}

+ 108 - 0
spring-el/src/main/java/org/springframework/expression/spel/SpelException.java

@@ -0,0 +1,108 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel;
+
+import org.springframework.expression.EvaluationException;
+
+/**
+ * Root exception for Spring EL related exceptions. Rather than holding a hard coded string indicating the problem, it
+ * records a message key and the inserts for the message. See {@link SpelMessages} for the list of all possible messages
+ * that can occur.
+ * 
+ * @author Andy Clement
+ * 
+ */
+public class SpelException extends EvaluationException {
+
+	private SpelMessages message;
+	private int position = -1;
+	private Object[] inserts;
+
+	public SpelException(int position, Throwable cause, SpelMessages message, Object... inserts) {
+		super(cause);
+		this.position = position;
+		this.message = message;
+		this.inserts = inserts;
+	}
+
+	public SpelException(Throwable cause, SpelMessages message, Object... inserts) {
+		super(cause);
+		this.message = message;
+		this.inserts = inserts;
+	}
+
+	public SpelException(int position, SpelMessages message, Object... inserts) {
+		super((Throwable)null);
+		this.position = position;
+		this.message = message;
+		this.inserts = inserts;
+	}
+
+	public SpelException(SpelMessages message, Object... inserts) {
+		super((Throwable)null);
+		this.message = message;
+		this.inserts = inserts;
+	}
+
+	public SpelException(String expressionString, int position, Throwable cause, SpelMessages message, Object... inserts) {
+		super(expressionString, cause);
+		this.position = position;
+		this.message = message;
+		this.inserts = inserts;
+	}
+
+	/**
+	 * @return a formatted message with inserts applied
+	 */
+	@Override
+	public String getMessage() {
+		if (message != null)
+			return message.formatMessage(position, inserts);
+		else
+			return super.getMessage();
+	}
+
+	/**
+	 * @return the position within the expression that gave rise to the exception (or -1 if unknown)
+	 */
+	public int getPosition() {
+		return this.position;
+	}
+
+	/**
+	 * @return the unformatted message
+	 */
+	public SpelMessages getMessageUnformatted() {
+		return this.message;
+	}
+
+	/**
+	 * Set the position in the related expression which gave rise to this exception.
+	 * 
+	 * @param position the position in the expression that gave rise to the exception
+	 */
+	public void setPosition(int position) {
+		this.position = position;
+	}
+
+	/**
+	 * @return the message inserts
+	 */
+	public Object[] getInserts() {
+		return inserts;
+	}
+
+}

+ 153 - 0
spring-el/src/main/java/org/springframework/expression/spel/SpelExpression.java

@@ -0,0 +1,153 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel;
+
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.Expression;
+import org.springframework.expression.common.ExpressionUtils;
+import org.springframework.expression.spel.ast.SpelNode;
+import org.springframework.expression.spel.standard.StandardEvaluationContext;
+
+/**
+ * A SpelExpressions represents a parsed (valid) expression that is ready to be evaluated in a specified context. An
+ * expression can be evaluated standalone or in a specified context. During expression evaluation the context may be
+ * asked to resolve references to types, beans, properties, methods.
+ * 
+ * @author Andy Clement
+ * 
+ */
+public class SpelExpression implements Expression {
+	private final String expression;
+	public final SpelNode ast;
+
+	/**
+	 * Construct an expression, only used by the parser.
+	 * 
+	 * @param expression
+	 * @param ast
+	 */
+	SpelExpression(String expression, SpelNode ast) {
+		this.expression = expression;
+		this.ast = ast;
+	}
+
+	/**
+	 * @return the expression string that was parsed to create this expression instance
+	 */
+	public String getExpressionString() {
+		return expression;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Object getValue() throws EvaluationException {
+		EvaluationContext eContext = new StandardEvaluationContext();
+		return ast.getValue(new ExpressionState(eContext));
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Object getValue(EvaluationContext context) throws EvaluationException {
+		return ast.getValue(new ExpressionState(context));
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Object getValue(EvaluationContext context, Class<?> expectedResultType) throws EvaluationException {
+		Object result = ast.getValue(new ExpressionState(context));
+
+		if (result != null && expectedResultType != null) {
+			Class<?> resultType = result.getClass();
+			if (expectedResultType.isAssignableFrom(resultType)) {
+				return result;
+			}
+			// Attempt conversion to the requested type, may throw an exception
+			return context.getTypeUtils().getTypeConverter().convertValue(result, expectedResultType);
+		}
+		return result;
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void setValue(EvaluationContext context, Object value) throws EvaluationException {
+		ast.setValue(new ExpressionState(context), value);
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean isWritable(EvaluationContext context) throws EvaluationException {
+		return ast.isWritable(new ExpressionState(context));
+	}
+
+	/**
+	 * @return return the Abstract Syntax Tree for the expression
+	 */
+	public SpelNode getAST() {
+		return ast;
+	}
+
+	/**
+	 * Produce a string representation of the Abstract Syntax Tree for the expression, this should ideally look like the
+	 * input expression, but properly formatted since any unnecessary whitespace will have been discarded during the
+	 * parse of the expression.
+	 * 
+	 * @return the string representation of the AST
+	 */
+	public String toStringAST() {
+		return ast.toStringAST();
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Class getValueType(EvaluationContext context) throws EvaluationException {
+		// TODO is this a legal implementation? The null return value could be very unhelpful. See other getValueType()
+		// also.
+		Object value = getValue(context);
+		if (value == null) {
+			return null;
+		} else {
+			return value.getClass();
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Class getValueType() throws EvaluationException {
+		Object value = getValue();
+		if (value == null) {
+			return null;
+		} else {
+			return value.getClass();
+		}
+	}
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public Object getValue(Class<?> expectedResultType) throws EvaluationException {
+		Object result = getValue();
+		return ExpressionUtils.convert(null, result, expectedResultType);
+	}
+
+}

+ 91 - 0
spring-el/src/main/java/org/springframework/expression/spel/SpelExpressionParser.java

@@ -0,0 +1,91 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel;
+
+import org.antlr.runtime.ANTLRStringStream;
+import org.antlr.runtime.CommonTokenStream;
+import org.antlr.runtime.RecognitionException;
+import org.springframework.expression.Expression;
+import org.springframework.expression.ParseException;
+import org.springframework.expression.ParserContext;
+import org.springframework.expression.common.DefaultNonTemplateParserContext;
+import org.springframework.expression.common.TemplateAwareExpressionParser;
+import org.springframework.expression.spel.ast.SpelNode;
+import org.springframework.expression.spel.generated.SpringExpressionsLexer;
+import org.springframework.expression.spel.generated.SpringExpressionsParser.expr_return;
+import org.springframework.expression.spel.internal.InternalELException;
+import org.springframework.expression.spel.internal.SpelTreeAdaptor;
+import org.springframework.expression.spel.internal.SpringExpressionsLexerExtender;
+import org.springframework.expression.spel.internal.SpringExpressionsParserExtender;
+
+/**
+ * Instances of this parser class can process Spring Expression Language format expressions. The result of parsing an
+ * expression is a SpelExpression instance that can be repeatedly evaluated (possibly against different evaluation
+ * contexts) or serialized for later evaluation.
+ * 
+ * @author Andy Clement
+ */
+public class SpelExpressionParser extends TemplateAwareExpressionParser {
+
+	private final SpringExpressionsLexer lexer;
+	private final SpringExpressionsParserExtender parser;
+
+	/**
+	 * Should be constructed through the SpelParserFactory
+	 */
+	public SpelExpressionParser() {
+		lexer = new SpringExpressionsLexerExtender();
+		CommonTokenStream tokens = new CommonTokenStream(lexer);
+		parser = new SpringExpressionsParserExtender(tokens);
+		parser.setTreeAdaptor(new SpelTreeAdaptor());
+	}
+
+	/**
+	 * Parse an expression string.
+	 * 
+	 * @param expressionString the expression to parse
+	 * @param context the parser context in which to perform the parse
+	 * @return a parsed expression object
+	 * @throws ParseException if the expression is invalid
+	 */
+	@Override
+	protected Expression doParseExpression(String expressionString, ParserContext context) throws ParseException {
+		try {
+			lexer.setCharStream(new ANTLRStringStream(expressionString));
+			CommonTokenStream tokens = new CommonTokenStream(lexer);
+			parser.setTokenStream(tokens);
+			expr_return exprReturn = parser.expr();
+			SpelExpression newExpression = new SpelExpression(expressionString, (SpelNode) exprReturn.getTree());
+			return newExpression;
+		} catch (RecognitionException re) {
+			ParseException exception = new ParseException(expressionString, "Recognition error at position: "
+					+ re.charPositionInLine + ": " + re.getMessage(), re);
+			throw exception;
+		} catch (InternalELException e) {
+			SpelException wrappedException = e.getCause();
+			throw new ParseException(expressionString, "Parsing problem: " + wrappedException.getMessage(),
+					wrappedException);
+		}
+	}
+
+	/**
+	 * Simple override with covariance to return a nicer type
+	 */
+	@Override
+	public SpelExpression parseExpression(String expressionString) throws ParseException {
+		return (SpelExpression) super.parseExpression(expressionString, DefaultNonTemplateParserContext.INSTANCE);
+	}
+}

+ 163 - 0
spring-el/src/main/java/org/springframework/expression/spel/SpelMessages.java

@@ -0,0 +1,163 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel;
+
+import java.text.MessageFormat;
+
+/**
+ * Contains all the messages that can be produced by the Spring Expression Language. Each message has a kind (info,
+ * warn, error) and a code number. Tests can be written to expect particular code numbers rather than particular text,
+ * enabling the message text to more easily be modified and the tests to run successfully in different locales.
+ * <p>
+ * When a message is formatted, it will have this kind of form
+ * 
+ * <pre>
+ * &lt;code&gt;
+ * EL1004E: (pos 34): Type cannot be found 'String'
+ * </pre>
+ * 
+ * </code> The prefix captures the code and the error kind, whilst the position is included if it is known and the
+ * message has had all relevant inserts applied to it.
+ * 
+ * @author Andy Clement
+ * 
+ */
+public enum SpelMessages {
+	// TODO put keys and messages into bundles for easy NLS
+	// TODO review if any messages are not used
+	// TODO sort messages into better groups if possible, sharing a name prefix perhaps
+
+	INITIALIZER_LENGTH_INCORRECT(Kind.ERROR, 1001,
+			"Array constructor call: initializer size of {0} does not match declared length of {1}"), TYPE_CONVERSION_ERROR(
+			Kind.ERROR, 1002, "Type conversion problem, cannot convert from {0} to {1}"), CONSTRUCTOR_NOT_FOUND(
+			Kind.ERROR, 1003, "Constructor call: No suitable constructor on type {0} for arguments {1}"), TYPE_NOT_FOUND(
+			Kind.ERROR, 1004, "Type cannot be found ''{0}''"), ADDITION_NOT_DEFINED(Kind.ERROR, 1005,
+			"Addition not defined between operands of type {0} and {1}"), METHOD_NOT_FOUND(Kind.ERROR, 1006,
+			"Method call: Method {0} cannot be found on {1} type"), ATTEMPTED_METHOD_CALL_ON_NULL_CONTEXT_OBJECT(
+			Kind.ERROR, 1007, "Method call: Attempted to call method {0} on null context object"), ATTEMPTED_PROPERTY_FIELD_REF_ON_NULL_CONTEXT_OBJECT(
+			Kind.ERROR, 1008,
+			"Field or property reference: Attempted to refer to field or property ''{0}'' on null context object"), PROPERTY_OR_FIELD_NOT_FOUND(
+			Kind.ERROR, 1009, "Field or property ''{0}'' cannot be found on object of type ''{1}''"), PROPERTY_OR_FIELD_SETTER_NOT_FOUND(
+			Kind.ERROR, 1010, "Field or property ''{0}'' cannot be set on object of type ''{1}''"), MULTIPLY_NOT_DEFINED(
+			Kind.ERROR, 1011, "Multiply not defined between operands of type {0} and {1}"), NOT_COMPARABLE(Kind.ERROR,
+			1012, "Cannot compare instances of {0} and {1}"), NOT_COMPARABLE_CANNOT_COERCE(Kind.ERROR, 1013,
+			"Cannot compare instances of {0} and {1} because they cannot be coerced to the same type"), VARIABLE_NOT_FOUND(
+			Kind.ERROR, 1014, "Variable named ''{0}'' cannot be found"), INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION(
+			Kind.ERROR, 1015, "Incorrect number of arguments for function, {0} supplied but function takes {1}"), NO_SUCH_FUNCTION(
+			Kind.ERROR, 1016, "No such function named ''{0}''"), NOT_A_FUNCTION(Kind.ERROR, 1017,
+			"The name ''{0}'' did not map to a function, it mapped to a ''{1}''"), INVALID_TYPE_FOR_SELECTION(
+			Kind.ERROR, 1018, "Cannot perform selection on input data of type ''{0}''"), RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN(
+			Kind.ERROR, 1019, "Result of selection criteria is not boolean"), MODULUS_NOT_DEFINED(Kind.ERROR, 1020,
+			"Modulus not defined between operands of type ''{0}'' and ''{1}''"), NULL_OPERAND_TO_OPERATOR(Kind.ERROR,
+			1021, "Operand evaluated to null and that is not supported for this operator"), NO_SIZE_OR_INITIALIZER_FOR_ARRAY_CONSTRUCTION(
+			Kind.ERROR, 1022, "No array size or initializer was supplied to construct the array"), INCORRECT_ELEMENT_TYPE_FOR_ARRAY(
+			Kind.ERROR, 1023, "The array of type ''{0}'' cannot have an element of type ''{1}'' inserted"), BETWEEN_RIGHT_OPERAND_MUST_BE_TWO_ELEMENT_LIST(
+			Kind.ERROR, 1024, "Right operand for the 'between' operator has to be a two-element list"), TYPE_NOT_SUPPORTED_BY_PROCESSOR(
+			Kind.ERROR, 1025,
+			"The collection processor ''{0}'' does not understand and input collection of elements of type {1}"), UNABLE_TO_ACCESS_FIELD(
+			Kind.ERROR, 1026, "Unable to access field ''{0}'' on type ''{1}''"), UNABLE_TO_ACCESS_PROPERTY_THROUGH_GETTER(
+			Kind.ERROR, 1027, "Unable to access property ''{0}'' through getter on type ''{1}''"), UNABLE_TO_ACCESS_PROPERTY_THROUGH_SETTER(
+			Kind.ERROR, 1028, "Unable to access property ''{0}'' through setter on type ''{1}''"), INVALID_PATTERN(
+			Kind.ERROR, 1029, "Pattern is not valid ''{0}''"), RECOGNITION_ERROR(Kind.ERROR, 1030,
+			"Recognition error: {0}"), // TODO poor message when a recognition exception occurs
+	PROJECTION_NOT_SUPPORTED_ON_TYPE(Kind.ERROR, 1031, "Projection is not supported on the type ''{0}''"), ARGLIST_SHOULD_NOT_BE_EVALUATED(
+			Kind.ERROR, 1032, "The argument list of a lambda expression should never have getValue() called upon it"), MAPENTRY_SHOULD_NOT_BE_EVALUATED(
+			Kind.ERROR, 1033, "A map entry should never have getValue() called upon it"), EXCEPTION_DURING_PROPERTY_READ(
+			Kind.ERROR, 1034, "A problem occurred whilst attempting to access the property ''{0}'': ''{1}''"), EXCEPTION_DURING_CONSTRUCTOR_INVOCATION(
+			Kind.ERROR, 1035, "A problem occurred whilst attempting to construct ''{0}'': ''{1}''"), DATE_CANNOT_BE_PARSED(
+			Kind.ERROR, 1036, "Unable to parse date ''{0}'' using format ''{1}''"), FUNCTION_REFERENCE_CANNOT_BE_INVOKED(
+			Kind.ERROR, 1037, "The function ''{0}'' mapped to an object of type ''{1}'' which cannot be invoked"), FUNCTION_NOT_DEFINED(
+			Kind.ERROR, 1038, "The function ''{0}'' could not be found"), EXCEPTION_DURING_FUNCTION_CALL(Kind.ERROR,
+			1039, "A problem occurred whilst attempting to invoke the function ''{0}'': ''{1}''"), ARRAY_INDEX_OUT_OF_BOUNDS(
+			Kind.ERROR, 1040, "The array has ''{0}'' elements, index ''{1}'' is invalid"), COLLECTION_INDEX_OUT_OF_BOUNDS(
+			Kind.ERROR, 1041, "The collection has ''{0}'' elements, index ''{1}'' is invalid"), STRING_INDEX_OUT_OF_BOUNDS(
+			Kind.ERROR, 1042, "The string has ''{0}'' characters, index ''{1}'' is invalid"), INDEXING_NOT_SUPPORTED_FOR_TYPE(
+			Kind.ERROR, 1043, "Indexing into type ''{0}'' is not supported"), OPERATOR_IN_CANNOT_DETERMINE_MEMBERSHIP(
+			Kind.ERROR, 1044, "Operator 'in' not implemented for detecting membership of a ''{0}'' in a ''{1}''"), CANNOT_NEGATE_TYPE(
+			Kind.ERROR, 1045, "Cannot determine negation of type ''{0}''"), CUT_ARGUMENTS_MUST_BE_INTS(Kind.ERROR,
+			1046, "Both arguments to the cut() processor must be Integers, but they are ''{0}'' and ''{1}''"), SOUNDSLIKE_NEEDS_STRING_OPERAND(
+			Kind.ERROR, 1047, "The soundslike operator needs String operands, but found a ''{0}''"), INSTANCEOF_OPERATOR_NEEDS_CLASS_OPERAND(
+			Kind.ERROR, 1048, "The operator 'instanceof' needs the right operand to be a class, not a ''{0}''"), LOCAL_VARIABLE_NOT_DEFINED(
+			Kind.ERROR, 1049, "Local variable named ''{0}'' could not be found"), EXCEPTION_DURING_METHOD_INVOCATION(
+			Kind.ERROR, 1050,
+			"A problem occurred when trying to execute method ''{0}'' on object of type ''{1}'': ''{2}''"), PLACEHOLDER_SHOULD_NEVER_BE_EVALUATED(
+			Kind.ERROR, 1051, "InternalError: A placeholder node in the Ast should never be evaluated!"), OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES(
+			Kind.ERROR, 1052, "The operator ''{0}'' is not supported between objects of type ''{1}'' and ''{2}''"), UNEXPECTED_PROBLEM_INVOKING_OPERATOR(
+			Kind.ERROR, 1054,
+			"Unexpected problem invoking operator ''{0}'' between objects of type ''{1}'' and ''{2}'': {3}"), PROBLEM_LOCATING_METHOD(
+			Kind.ERROR, 1055, "Problem locating method {0} cannot on type {1}"), PROBLEM_LOCATING_CONSTRUCTOR(
+			Kind.ERROR, 1056,
+			"A problem occurred whilst attempting to construct an object of type ''{0}'' using arguments ''{1}''"), INVALID_FIRST_OPERAND_FOR_LIKE_OPERATOR(
+			Kind.ERROR, 1057, "First operand to like operator must be a string.  ''{0}'' is not"), INVALID_SECOND_OPERAND_FOR_LIKE_OPERATOR(
+			Kind.ERROR, 1058, "Second operand to like operator must be a string (regex). ''{0}'' is not"), SETVALUE_NOT_SUPPORTED(
+			Kind.ERROR, 1059, "setValue(ExpressionState, Object) not implemented for ''{0}''  (''{1}''"), TYPE_NAME_EXPECTED_FOR_ARRAY_CONSTRUCTION(
+			Kind.ERROR, 1060, "Expected the type of the new array to be specified as a String but found ''{0}''"), PROBLEM_DURING_TYPE_CONVERSION(
+			Kind.ERROR, 1061, "Problem occurred during type conversion: {0}"), MULTIPLE_POSSIBLE_METHODS(Kind.ERROR,
+			1062, "Method call of ''{0}'' is ambiguous, supported type conversions allow multiple variants to match"), EXCEPTION_DURING_PROPERTY_WRITE(
+			Kind.ERROR, 1063, "A problem occurred whilst attempting to set the property ''{0}'': ''{1}''"), NOT_AN_INTEGER(
+			Kind.ERROR, 1064, "The value ''{0}'' cannot be parsed as an int"), NOT_A_LONG(Kind.ERROR, 1065,
+			"The value ''{0}'' cannot be parsed as a long"), PARSE_PROBLEM(Kind.ERROR, 1066,
+			"Error occurred during expression parse: {0}"), INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR(Kind.ERROR,
+			1067, "First operand to matches operator must be a string.  ''{0}'' is not"), INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR(
+			Kind.ERROR, 1068, "Second operand to matches operator must be a string. ''{0}'' is not"), FUNCTION_MUST_BE_STATIC(
+			Kind.ERROR,
+			1069,
+			"Only static methods can be called via function references. The method ''{0}'' referred to by name ''{1}'' is not static.");
+
+	private Kind kind;
+	private int code;
+	private String message;
+
+	public static enum Kind {
+		INFO, WARNING, ERROR
+	};
+
+	private SpelMessages(Kind kind, int code, String message) {
+		this.kind = kind;
+		this.code = code;
+		this.message = message;
+	}
+
+	/**
+	 * Produce a complete message including the prefix, the position (if known) and with the inserts applied to the
+	 * message.
+	 * 
+	 * @param pos the position, if less than zero it is ignored and not included in the message
+	 * @param inserts the inserts to put into the formatted message
+	 * @return a formatted message
+	 */
+	public String formatMessage(int pos, Object... inserts) {
+		StringBuilder formattedMessage = new StringBuilder();
+		formattedMessage.append("EL").append(code);
+		switch (kind) {
+		case WARNING:
+			formattedMessage.append("W");
+			break;
+		case INFO:
+			formattedMessage.append("I");
+			break;
+		case ERROR:
+			formattedMessage.append("E");
+			break;
+		}
+		formattedMessage.append(":");
+		if (pos != -1) {
+			formattedMessage.append("(pos ").append(pos).append("): ");
+		}
+		formattedMessage.append(MessageFormat.format(message, inserts));
+		return formattedMessage.toString();
+	}
+}

+ 58 - 0
spring-el/src/main/java/org/springframework/expression/spel/SpelUtilities.java

@@ -0,0 +1,58 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel;
+
+import java.io.PrintStream;
+
+import org.springframework.expression.spel.ast.SpelNode;
+
+/**
+ * Utilities for working with Spring Expressions.
+ * 
+ * @author Andy Clement
+ * 
+ */
+public class SpelUtilities {
+
+	/**
+	 * Output an indented representation of the expression syntax tree to the specified output stream.
+	 * 
+	 * @param printStream the output stream to print into
+	 * @param expression the expression to be displayed
+	 */
+	public static void printAbstractSyntaxTree(PrintStream printStream, SpelExpression expression) {
+		printStream.println("===> Expression '" + expression.getExpressionString() + "' - AST start");
+		printAST(printStream, expression.getAST(), "");
+		printStream.println("===> Expression '" + expression.getExpressionString() + "' - AST end");
+	}
+
+	/*
+	 * Helper method for printing the AST with indentation
+	 */
+	private static void printAST(PrintStream out, SpelNode t, String indent) {
+		if (t != null) {
+			StringBuffer sb = new StringBuffer();
+			String s = (t.getType() == -1 ? "EOF" : t.getClass().getSimpleName());
+			sb.append(indent).append(s);
+			sb.append("  value=").append(t.getText());
+			sb.append(t.getChildCount() < 2 ? "" : "  children=#" + t.getChildCount());
+			out.println(sb.toString());
+			for (int i = 0; i < t.getChildCount(); i++) {
+				printAST(out, t.getChild(i), indent + "  ");
+			}
+		}
+	}
+}

+ 54 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/Assign.java

@@ -0,0 +1,54 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.ExpressionState;
+
+/**
+ * Represents assignment. An alternative to calling setValue() for an expression is to use an assign.
+ * <p>
+ * Example: 'someNumberProperty=42'
+ * 
+ * @author Andy Clement
+ * 
+ */
+public class Assign extends SpelNode {
+
+	public Assign(Token payload) {
+		super(payload);
+	}
+
+	@Override
+	public Object getValue(ExpressionState state) throws EvaluationException {
+		Object newValue = getChild(1).getValue(state);
+		getChild(0).setValue(state, newValue);
+		return newValue;
+	}
+
+	@Override
+	public String toStringAST() {
+		return new StringBuilder().append(getChild(0).toStringAST()).append("=").append(getChild(1).toStringAST())
+				.toString();
+	}
+
+	@Override
+	public boolean isWritable(ExpressionState expressionState) throws SpelException {
+		return false;
+	}
+}

+ 40 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/BooleanLiteral.java

@@ -0,0 +1,40 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+
+/**
+ * Represents the literal values TRUE and FALSE.
+ * 
+ * @author Andy Clement
+ * 
+ */
+public class BooleanLiteral extends Literal {
+
+	private final Boolean value;
+
+	public BooleanLiteral(Token payload, boolean value) {
+		super(payload);
+		this.value = value;
+	}
+
+	@Override
+	public Boolean getLiteralValue() {
+		return value;
+	}
+
+}

+ 120 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/CompoundExpression.java

@@ -0,0 +1,120 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.ExpressionState;
+
+/**
+ * Represents a DOT separated expression sequence, such as 'property1.property2.methodOne()'
+ * 
+ * @author Andy Clement
+ * 
+ */
+public class CompoundExpression extends SpelNode {
+
+	public CompoundExpression(Token payload) {
+		super(payload);
+	}
+
+	/**
+	 * Evalutes a compound expression. This involves evaluating each piece in turn and the return value from each piece
+	 * is the active context object for the subsequent piece.
+	 * 
+	 * @param state the state in which the expression is being evaluated
+	 * @return the final value from the last piece of the compound expression
+	 */
+	@Override
+	public Object getValue(ExpressionState state) throws EvaluationException {
+		Object result = null;
+		SpelNode nextNode = null;
+		try {
+			nextNode = getChild(0);
+			result = nextNode.getValue(state);
+			for (int i = 1; i < getChildCount(); i++) {
+				try {
+					state.pushActiveContextObject(result);
+					nextNode = getChild(i);
+					result = nextNode.getValue(state);
+				} finally {
+					state.popActiveContextObject();
+				}
+			}
+		} catch (SpelException ee) {
+			// Correct the position for the error before rethrowing
+			ee.setPosition(nextNode.getCharPositionInLine());
+			throw ee;
+		}
+		return result;
+	}
+
+	@Override
+	public void setValue(ExpressionState state, Object value) throws EvaluationException {
+		if (getChildCount() == 1) {
+			getChild(0).setValue(state, value);
+			return;
+		}
+		Object ctx = getChild(0).getValue(state);
+		for (int i = 1; i < getChildCount() - 1; i++) {
+			try {
+				state.pushActiveContextObject(ctx);
+				ctx = getChild(i).getValue(state);
+			} finally {
+				state.popActiveContextObject();
+			}
+		}
+		try {
+			state.pushActiveContextObject(ctx);
+			getChild(getChildCount() - 1).setValue(state, value);
+		} finally {
+			state.popActiveContextObject();
+		}
+	}
+
+	@Override
+	public boolean isWritable(ExpressionState state) throws EvaluationException {
+		if (getChildCount() == 1) {
+			return getChild(0).isWritable(state);
+		}
+		Object ctx = getChild(0).getValue(state);
+		for (int i = 1; i < getChildCount() - 1; i++) {
+			try {
+				state.pushActiveContextObject(ctx);
+				ctx = getChild(i).getValue(state);
+			} finally {
+				state.popActiveContextObject();
+			}
+		}
+		try {
+			state.pushActiveContextObject(ctx);
+			return getChild(getChildCount() - 1).isWritable(state);
+		} finally {
+			state.popActiveContextObject();
+		}
+	}
+
+	@Override
+	public String toStringAST() {
+		StringBuffer sb = new StringBuffer();
+		for (int i = 0; i < getChildCount(); i++) {
+			sb.append(getChild(i).toStringAST());
+		}
+		return sb.toString();
+	}
+
+}

+ 317 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java

@@ -0,0 +1,317 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import java.lang.reflect.Array;
+import java.util.List;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.AccessException;
+import org.springframework.expression.ConstructorExecutor;
+import org.springframework.expression.ConstructorResolver;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.spel.ExpressionState;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.SpelMessages;
+import org.springframework.expression.spel.internal.TypeCode;
+import org.springframework.expression.spel.internal.Utils;
+
+/**
+ * Represents the invocation of a constructor. Either a constructor on a regular type or construction of an array. When
+ * an array is constructed, an initializer can be specified.
+ * <p>
+ * Examples:<br>
+ * new String('hello world')<br>
+ * new int[]{1,2,3,4}<br>
+ * new int[3] new int[3]{1,2,3}
+ * 
+ * @author Andy Clement
+ * 
+ */
+public class ConstructorReference extends SpelNode {
+
+	/**
+	 * The resolver/executor model {@link ConstructorResolver} supports the caching of executor objects that can run
+	 * some discovered constructor repeatedly without searching for it each time. This flag controls whether caching
+	 * occurs and is primarily exposed for testing.
+	 */
+	public static boolean useCaching = true;
+
+	/**
+	 * The cached executor that may be reused on subsequent evaluations.
+	 */
+	private ConstructorExecutor cachedExecutor;
+
+	/**
+	 * If true then this is an array constructor, for example, 'new String[]', rather than a simple constructor 'new
+	 * String()'
+	 */
+	private final boolean isArrayConstructor;
+
+	public ConstructorReference(Token payload, boolean isArrayConstructor) {
+		super(payload);
+		this.isArrayConstructor = isArrayConstructor;
+	}
+
+	/**
+	 * Implements getValue() - delegating to the code for building an array or a simple type.
+	 */
+	@Override
+	public Object getValue(ExpressionState state) throws EvaluationException {
+		if (isArrayConstructor) {
+			return createArray(state);
+		} else {
+			return createNewInstance(state);
+		}
+	}
+
+	@Override
+	public String toStringAST() {
+		StringBuilder sb = new StringBuilder();
+		sb.append("new ");
+
+		int index = 0;
+		sb.append(getChild(index++).toStringAST());
+
+		if (!isArrayConstructor) {
+			sb.append("(");
+			for (int i = index; i < getChildCount(); i++) {
+				if (i > index)
+					sb.append(",");
+				sb.append(getChild(i).toStringAST());
+			}
+			sb.append(")");
+		} else {
+			// Next child is EXPRESSIONLIST token with children that are the
+			// expressions giving array size
+			sb.append("[");
+			SpelNode arrayRank = getChild(index++);
+			for (int i = 0; i < arrayRank.getChildCount(); i++) {
+				if (i > 0)
+					sb.append(",");
+				sb.append(arrayRank.getChild(i).toStringAST());
+			}
+			sb.append("]");
+			if (index < getChildCount())
+				sb.append(" ").append(getChild(index).toStringAST());
+		}
+		return sb.toString();
+	}
+
+	@Override
+	public boolean isWritable(ExpressionState expressionState) throws SpelException {
+		return false;
+	}
+
+	/**
+	 * Create an array and return it. The children of this node indicate the type of array, the array ranks and any
+	 * optional initializer that might have been supplied.
+	 * 
+	 * @param state the expression state within which this expression is being evaluated
+	 * @return the new array
+	 * @throws EvaluationException if there is a problem creating the array
+	 */
+	private Object createArray(ExpressionState state) throws EvaluationException {
+		Object intendedArrayType = getChild(0).getValue(state);
+		if (!(intendedArrayType instanceof String)) {
+			throw new SpelException(getChild(0).getCharPositionInLine(),
+					SpelMessages.TYPE_NAME_EXPECTED_FOR_ARRAY_CONSTRUCTION, Utils
+							.formatClassnameForMessage(intendedArrayType.getClass()));
+		}
+		String type = (String) intendedArrayType;
+		Class<?> componentType = null;
+		TypeCode arrayTypeCode = TypeCode.forName(type);
+		if (arrayTypeCode == TypeCode.OBJECT) {
+			componentType = state.findType(type);
+		} else {
+			componentType = arrayTypeCode.getType();
+		}
+
+		Object newArray = null;
+
+		if (getChild(1).getChildCount() == 0) { // are the array ranks defined?
+			if (getChildCount() < 3) {
+				throw new SpelException(getCharPositionInLine(),
+						SpelMessages.NO_SIZE_OR_INITIALIZER_FOR_ARRAY_CONSTRUCTION);
+			}
+			// no array ranks so use the size of the initializer to determine array size
+			int arraySize = getChild(2).getChildCount();
+			newArray = Array.newInstance(componentType, arraySize);
+		} else {
+			// Array ranks are specified but is it a single or multiple dimension array?
+			int dimensions = getChild(1).getChildCount();
+			if (dimensions == 1) {
+				Object o = getChild(1).getValue(state);
+				int arraySize = state.toInteger(o);
+				if (getChildCount() == 3) {
+					// Check initializer length matches array size length
+					int initializerLength = getChild(2).getChildCount();
+					if (initializerLength != arraySize) {
+						throw new SpelException(getChild(2).getCharPositionInLine(),
+								SpelMessages.INITIALIZER_LENGTH_INCORRECT, initializerLength, arraySize);
+					}
+				}
+				newArray = Array.newInstance(componentType, arraySize);
+			} else {
+				// Multi-dimensional - hold onto your hat !
+				int[] dims = new int[dimensions];
+				for (int d = 0; d < dimensions; d++) {
+					dims[d] = state.toInteger(getChild(1).getChild(d).getValue(state));
+				}
+				newArray = Array.newInstance(componentType, dims);
+				// TODO check any specified initializer for the multidim array matches
+			}
+		}
+
+		// Populate the array using the initializer if one is specified
+		if (getChildCount() == 3) {
+			SpelNode initializer = getChild(2);
+			if (arrayTypeCode == TypeCode.OBJECT) {
+				Object[] newObjectArray = (Object[]) newArray;
+				for (int i = 0; i < newObjectArray.length; i++) {
+					SpelNode elementNode = initializer.getChild(i);
+					Object arrayEntry = elementNode.getValue(state);
+					if (!componentType.isAssignableFrom(arrayEntry.getClass())) {
+						throw new SpelException(elementNode.getCharPositionInLine(),
+								SpelMessages.INCORRECT_ELEMENT_TYPE_FOR_ARRAY, componentType.getName(), arrayEntry
+										.getClass().getName());
+					}
+					newObjectArray[i] = arrayEntry;
+				}
+			} else if (arrayTypeCode == TypeCode.INT) {
+				int[] newIntArray = (int[]) newArray;
+				for (int i = 0; i < newIntArray.length; i++) {
+					newIntArray[i] = state.toInteger(initializer.getChild(i).getValue(state));
+				}
+			} else if (arrayTypeCode == TypeCode.BOOLEAN) {
+				boolean[] newBooleanArray = (boolean[]) newArray;
+				for (int i = 0; i < newBooleanArray.length; i++) {
+					newBooleanArray[i] = state.toBoolean(initializer.getChild(i).getValue(state));
+				}
+			} else if (arrayTypeCode == TypeCode.CHAR) {
+				char[] newCharArray = (char[]) newArray;
+				for (int i = 0; i < newCharArray.length; i++) {
+					newCharArray[i] = state.toCharacter(initializer.getChild(i).getValue(state));
+				}
+			} else if (arrayTypeCode == TypeCode.SHORT) {
+				short[] newShortArray = (short[]) newArray;
+				for (int i = 0; i < newShortArray.length; i++) {
+					newShortArray[i] = state.toShort(initializer.getChild(i).getValue(state));
+				}
+			} else if (arrayTypeCode == TypeCode.LONG) {
+				long[] newLongArray = (long[]) newArray;
+				for (int i = 0; i < newLongArray.length; i++) {
+					newLongArray[i] = state.toLong(initializer.getChild(i).getValue(state));
+				}
+			} else if (arrayTypeCode == TypeCode.FLOAT) {
+				float[] newFloatArray = (float[]) newArray;
+				for (int i = 0; i < newFloatArray.length; i++) {
+					newFloatArray[i] = state.toFloat(initializer.getChild(i).getValue(state));
+				}
+			} else if (arrayTypeCode == TypeCode.DOUBLE) {
+				double[] newDoubleArray = (double[]) newArray;
+				for (int i = 0; i < newDoubleArray.length; i++) {
+					newDoubleArray[i] = state.toDouble(initializer.getChild(i).getValue(state));
+				}
+			} else if (arrayTypeCode == TypeCode.BYTE) {
+				byte[] newByteArray = (byte[]) newArray;
+				for (int i = 0; i < newByteArray.length; i++) {
+					newByteArray[i] = state.toByte(initializer.getChild(i).getValue(state));
+				}
+			}
+		}
+
+		return newArray;
+	}
+
+	/**
+	 * Create a new ordinary object and return it.
+	 * 
+	 * @param state the expression state within which this expression is being evaluated
+	 * @return the new object
+	 * @throws EvaluationException if there is a problem creating the object
+	 */
+	private Object createNewInstance(ExpressionState state) throws EvaluationException {
+		Object[] arguments = new Object[getChildCount() - 1];
+		Class<?>[] argumentTypes = new Class[getChildCount() - 1];
+		for (int i = 0; i < arguments.length; i++) {
+			Object childValue = getChild(i + 1).getValue(state);
+			arguments[i] = childValue;
+			argumentTypes[i] = childValue.getClass();
+		}
+
+		if (cachedExecutor != null) {
+			try {
+				return cachedExecutor.execute(state.getEvaluationContext(), arguments);
+			} catch (AccessException ae) {
+				// this is OK - it may have gone stale due to a class change,
+				// let's try to get a new one and call it before giving up
+			}
+		}
+
+		// either there was no accessor or it no longer exists
+		String typename = (String) getChild(0).getValue(state);
+		cachedExecutor = findExecutorForConstructor(typename, argumentTypes, state);
+		try {
+			return cachedExecutor.execute(state.getEvaluationContext(), arguments);
+		} catch (AccessException ae) {
+			throw new SpelException(ae, SpelMessages.EXCEPTION_DURING_CONSTRUCTOR_INVOCATION, typename, ae.getMessage());
+		} finally {
+			if (!useCaching) {
+				cachedExecutor = null;
+			}
+		}
+	}
+
+	/**
+	 * Go through the list of registered constructor resolvers and see if any can find a constructor that takes the
+	 * specified set of arguments.
+	 * 
+	 * @param typename the type trying to be constructed
+	 * @param argumentTypes the types of the arguments supplied that the constructor must take
+	 * @param state the current state of the expression
+	 * @return a reusable ConstructorExecutor that can be invoked to run the constructor or null
+	 * @throws SpelException if there is a problem locating the constructor
+	 */
+	public ConstructorExecutor findExecutorForConstructor(String typename, Class<?>[] argumentTypes,
+			ExpressionState state) throws SpelException {
+		EvaluationContext eContext = state.getEvaluationContext();
+		List<ConstructorResolver> cResolvers = eContext.getConstructorResolvers();
+		if (cResolvers != null) {
+			for (ConstructorResolver ctorResolver : cResolvers) {
+				try {
+					ConstructorExecutor cEx = ctorResolver.resolve(state.getEvaluationContext(), typename,
+							argumentTypes);
+					if (cEx != null) {
+						return cEx;
+					}
+				} catch (AccessException e) {
+					Throwable cause = e.getCause();
+					if (cause instanceof SpelException) {
+						throw (SpelException) cause;
+					} else {
+						throw new SpelException(cause, SpelMessages.PROBLEM_LOCATING_CONSTRUCTOR, typename, Utils
+								.formatMethodForMessage("", argumentTypes));
+					}
+				}
+			}
+		}
+		throw new SpelException(SpelMessages.CONSTRUCTOR_NOT_FOUND, typename, Utils.formatMethodForMessage("",
+				argumentTypes));
+	}
+
+}

+ 46 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/Dot.java

@@ -0,0 +1,46 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.ExpressionState;
+
+/**
+ * This is used for preserving positional information from the input expression.
+ * 
+ * @author Andy Clement
+ * 
+ */
+public class Dot extends SpelNode {
+	// TODO Keep Dot for the positional information or remove it?
+
+	public Dot(Token payload) {
+		super(payload);
+	}
+
+	@Override
+	public String toStringAST() {
+		return ".";
+	}
+
+	@Override
+	public Object getValue(ExpressionState state) throws SpelException {
+		// This makes Dot a do-nothing operation, but this is not free in terms of computation
+		return state.getActiveContextObject();
+	}
+
+}

+ 147 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/FunctionReference.java

@@ -0,0 +1,147 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.TypeConverter;
+import org.springframework.expression.spel.ExpressionState;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.SpelMessages;
+import org.springframework.expression.spel.reflection.ReflectionUtils;
+
+/**
+ * A function reference is of the form "#someFunction(a,b,c)". Functions may be defined in the context prior to the
+ * expression being evaluated or within the expression itself using a lambda function definition. For example: Lambda
+ * function definition in an expression: "(#max = {|x,y|$x>$y?$x:$y};max(2,3))" Calling context defined function:
+ * "#isEven(37)". Functions may also be static java methods, registered in the context prior to invocation of the
+ * expression.
+ * 
+ * Functions are very simplistic, the arguments are not part of the definition (right now), so the names must be unique.
+ * 
+ * @author Andy Clement
+ */
+public class FunctionReference extends SpelNode {
+
+	private final String name;
+
+	public FunctionReference(Token payload) {
+		super(payload);
+		name = payload.getText();
+	}
+
+	@Override
+	public Object getValue(ExpressionState state) throws EvaluationException {
+		Object o = state.lookupVariable(name);
+		if (o == null) {
+			throw new SpelException(SpelMessages.FUNCTION_NOT_DEFINED, name);
+		}
+
+		// Two possibilities: a lambda function or a Java static method registered as a function
+		if (!(o instanceof Method)) {
+			throw new SpelException(SpelMessages.FUNCTION_REFERENCE_CANNOT_BE_INVOKED, name, o.getClass());
+		}
+
+		return executeFunctionJLRMethod(state, (Method) o);
+	}
+
+	/**
+	 * Execute a function represented as a java.lang.reflect.Method.
+	 * 
+	 * @param state the expression evaluation state
+	 * @param the java method to invoke
+	 * @return the return value of the invoked Java method
+	 * @throws EvaluationException if there is any problem invoking the method
+	 */
+	private Object executeFunctionJLRMethod(ExpressionState state, Method m) throws EvaluationException {
+		Object[] functionArgs = getArguments(state);
+
+		if (!m.isVarArgs() && m.getParameterTypes().length != functionArgs.length) {
+			throw new SpelException(SpelMessages.INCORRECT_NUMBER_OF_ARGUMENTS_TO_FUNCTION, functionArgs.length, m
+					.getParameterTypes().length);
+		}
+		// Only static methods can be called in this way
+		if (!Modifier.isStatic(m.getModifiers())) {
+			throw new SpelException(getCharPositionInLine(), SpelMessages.FUNCTION_MUST_BE_STATIC, m
+					.getDeclaringClass().getName()
+					+ "." + m.getName(), name);
+		}
+
+		// Convert arguments if necessary and remap them for varargs if required
+		if (functionArgs != null) {
+			EvaluationContext ctx = state.getEvaluationContext();
+			TypeConverter converter = null;
+			if (ctx.getTypeUtils() != null) {
+				converter = ctx.getTypeUtils().getTypeConverter();
+			}
+			ReflectionUtils.convertArguments(m.getParameterTypes(), m.isVarArgs(), converter, functionArgs);
+		}
+		if (m.isVarArgs()) {
+			functionArgs = ReflectionUtils.setupArgumentsForVarargsInvocation(m.getParameterTypes(), functionArgs);
+		}
+
+		try {
+			return m.invoke(m.getClass(), functionArgs);
+		} catch (IllegalArgumentException e) {
+			throw new SpelException(getCharPositionInLine(), e, SpelMessages.EXCEPTION_DURING_FUNCTION_CALL, name, e
+					.getMessage());
+		} catch (IllegalAccessException e) {
+			throw new SpelException(getCharPositionInLine(), e, SpelMessages.EXCEPTION_DURING_FUNCTION_CALL, name, e
+					.getMessage());
+		} catch (InvocationTargetException e) {
+			throw new SpelException(getCharPositionInLine(), e, SpelMessages.EXCEPTION_DURING_FUNCTION_CALL, name, e
+					.getMessage());
+		}
+	}
+
+	@Override
+	public String toStringAST() {
+		StringBuilder sb = new StringBuilder("#").append(name);
+		sb.append("(");
+		for (int i = 0; i < getChildCount(); i++) {
+			if (i > 0)
+				sb.append(",");
+			sb.append(getChild(i).toStringAST());
+		}
+		sb.append(")");
+		return sb.toString();
+	}
+
+	// to 'assign' to a function don't use the () suffix and so it is just a variable reference
+	@Override
+	public boolean isWritable(ExpressionState expressionState) throws EvaluationException {
+		return false;
+	}
+
+	/**
+	 * Compute the arguments to the function, they are the children of this expression node.
+	 * @return an array of argument values for the function call
+	 */
+	private Object[] getArguments(ExpressionState state) throws EvaluationException {
+		// Compute arguments to the function
+		Object[] arguments = new Object[getChildCount()];
+		for (int i = 0; i < arguments.length; i++) {
+			arguments[i] = getChild(i).getValue(state);
+		}
+		return arguments;
+	}
+
+}

+ 41 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/Identifier.java

@@ -0,0 +1,41 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.ExpressionState;
+
+public class Identifier extends SpelNode {
+
+	private final String id;
+
+	public Identifier(Token payload) {
+		super(payload);
+		id = payload.getText();
+	}
+
+	@Override
+	public String toStringAST() {
+		return id;
+	}
+
+	@Override
+	public String getValue(ExpressionState state) throws SpelException {
+		return id;
+	}
+
+}

+ 156 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/Indexer.java

@@ -0,0 +1,156 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.spel.ExpressionState;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.SpelMessages;
+
+// TODO support multidimensional arrays
+// TODO support correct syntax for multidimensional [][][] and not [,,,]
+/**
+ * An Indexer can index into some proceeding structure to access a particular piece of it. Supported structures are:
+ * strings/collections (lists/sets)/arrays
+ * 
+ * @author Andy Clement
+ */
+public class Indexer extends SpelNode {
+
+	public Indexer(Token payload) {
+		super(payload);
+	}
+
+	@Override
+	public Object getValue(ExpressionState state) throws EvaluationException {
+		Object ctx = state.getActiveContextObject();
+		Object index = getChild(0).getValue(state);
+
+		// Indexing into a Map
+		if (ctx instanceof Map) {
+			return ((Map<?, ?>) ctx).get(index);
+		}
+
+		int idx = state.toInteger(index);
+
+		if (ctx.getClass().isArray()) {
+			return accessArrayElement(ctx, idx);
+		} else if (ctx instanceof Collection) {
+			Collection<?> c = (Collection<?>) ctx;
+			if (idx >= c.size()) {
+				throw new SpelException(SpelMessages.COLLECTION_INDEX_OUT_OF_BOUNDS, c.size(), idx);
+			}
+			int pos = 0;
+			for (Object o : c) {
+				if (pos == idx) {
+					return o;
+				}
+				pos++;
+			}
+			// } else if (ctx instanceof Map) {
+			// Map<?,?> c = (Map<?,?>) ctx;
+			// // This code would allow a key/value pair to be pulled out by index from a map
+			// if (idx >= c.size()) {
+			// throw new ELException(ELMessages.COLLECTION_INDEX_OUT_OF_BOUNDS,c.size(),idx);
+			// }
+			// Set<?> keys = c.keySet();
+			// int pos = 0;
+			// for (Object k : keys) {
+			// if (pos==idx) {
+			// return new KeyValuePair(k,c.get(k));
+			// }
+			// pos++;
+			// }
+		} else if (ctx instanceof String) {
+			String ctxString = (String) ctx;
+			if (idx >= ctxString.length()) {
+				throw new SpelException(SpelMessages.STRING_INDEX_OUT_OF_BOUNDS, ctxString.length(), idx);
+			}
+			return String.valueOf(ctxString.charAt(idx));
+		}
+		throw new SpelException(SpelMessages.INDEXING_NOT_SUPPORTED_FOR_TYPE, ctx.getClass().getName());
+	}
+
+	private Object accessArrayElement(Object ctx, int idx) throws SpelException {
+		Class<?> arrayComponentType = ctx.getClass().getComponentType();
+		if (arrayComponentType == Integer.TYPE) {
+			int[] array = (int[]) ctx;
+			checkAccess(array.length, idx);
+			return array[idx];
+		} else if (arrayComponentType == Boolean.TYPE) {
+			boolean[] array = (boolean[]) ctx;
+			checkAccess(array.length, idx);
+			return array[idx];
+		} else if (arrayComponentType == Character.TYPE) {
+			char[] array = (char[]) ctx;
+			checkAccess(array.length, idx);
+			return array[idx];
+		} else if (arrayComponentType == Long.TYPE) {
+			long[] array = (long[]) ctx;
+			checkAccess(array.length, idx);
+			return array[idx];
+		} else if (arrayComponentType == Short.TYPE) {
+			short[] array = (short[]) ctx;
+			checkAccess(array.length, idx);
+			return array[idx];
+		} else if (arrayComponentType == Double.TYPE) {
+			double[] array = (double[]) ctx;
+			checkAccess(array.length, idx);
+			return array[idx];
+		} else if (arrayComponentType == Float.TYPE) {
+			float[] array = (float[]) ctx;
+			checkAccess(array.length, idx);
+			return array[idx];
+		} else if (arrayComponentType == Byte.TYPE) {
+			byte[] array = (byte[]) ctx;
+			checkAccess(array.length, idx);
+			return array[idx];
+		} else {
+			Object[] array = (Object[]) ctx;
+			checkAccess(array.length, idx);
+			return array[idx];
+		}
+	}
+
+	@Override
+	public String toStringAST() {
+		StringBuilder sb = new StringBuilder();
+		sb.append("[");
+		for (int i = 0; i < getChildCount(); i++) {
+			if (i > 0)
+				sb.append(",");
+			sb.append(getChild(i).toStringAST());
+		}
+		sb.append("]");
+		return sb.toString();
+	}
+
+	@Override
+	public boolean isWritable(ExpressionState expressionState) throws SpelException {
+		return false;
+	}
+
+	private void checkAccess(int arrayLength, int index) throws SpelException {
+		if (index > arrayLength) {
+			throw new SpelException(getCharPositionInLine(), SpelMessages.ARRAY_INDEX_OUT_OF_BOUNDS, arrayLength, index);
+		}
+	}
+
+}

+ 39 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/IntLiteral.java

@@ -0,0 +1,39 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+
+/**
+ * Expression language AST node that represents an integer literal.
+ * 
+ * @author Andy Clement
+ */
+public class IntLiteral extends Literal {
+
+	private final Integer value;
+
+	IntLiteral(Token payload, int value) {
+		super(payload);
+		this.value = value;
+	}
+
+	@Override
+	public Integer getLiteralValue() {
+		return value;
+	}
+
+}

+ 101 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/Literal.java

@@ -0,0 +1,101 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.spel.ExpressionState;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.SpelMessages;
+import org.springframework.expression.spel.internal.InternalELException;
+
+/**
+ * Common superclass for nodes representing literals (boolean, string, number, etc).
+ * 
+ * @author Andy Clement
+ * 
+ */
+public abstract class Literal extends SpelNode {
+
+	public Literal(Token payload) {
+		super(payload);
+	}
+
+	public abstract Object getLiteralValue();
+
+	@Override
+	public final Object getValue(ExpressionState state) throws SpelException {
+		return getLiteralValue();
+	}
+
+	@Override
+	public String toString() {
+		return getLiteralValue().toString();
+	}
+
+	@Override
+	public String toStringAST() {
+		return toString();
+	}
+
+	@Override
+	public boolean isWritable(ExpressionState expressionState) throws SpelException {
+		return false;
+	}
+
+	/**
+	 * Process the string form of a number, using the specified base if supplied and return an appropriate literal to
+	 * hold it. Any suffix to indicate a long will be taken into account (either 'l' or 'L' is supported).
+	 * 
+	 * @param numberToken the token holding the number as its payload (eg. 1234 or 0xCAFE)
+	 * @param radix the base of number
+	 * @return a subtype of Literal that can represent it
+	 */
+	public static Literal getIntLiteral(Token numberToken, int radix) {
+		String numberString = numberToken.getText();
+
+		boolean isLong = false;
+		boolean isHex = (radix == 16);
+
+		if (numberString.length() > 0) {
+			isLong = numberString.endsWith("L") || numberString.endsWith("l");
+		}
+
+		if (isLong || isHex) { // needs to be chopped up a little
+			int len = numberString.length();
+			// assert: if hex then startsWith 0x or 0X
+			numberString = numberString.substring((isHex ? 2 : 0), isLong ? len - 1 : len);
+		}
+
+		if (isLong) {
+			try {
+				long value = Long.parseLong(numberString, radix);
+				return new LongLiteral(numberToken, value);
+			} catch (NumberFormatException nfe) {
+				throw new InternalELException(new SpelException(numberToken.getCharPositionInLine(), nfe,
+						SpelMessages.NOT_A_LONG, numberToken.getText()));
+			}
+		} else {
+			try {
+				int value = Integer.parseInt(numberString, radix);
+				return new IntLiteral(numberToken, value);
+			} catch (NumberFormatException nfe) {
+				throw new InternalELException(new SpelException(numberToken.getCharPositionInLine(), nfe,
+						SpelMessages.NOT_AN_INTEGER, numberToken.getText()));
+			}
+		}
+	}
+
+}

+ 39 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/LongLiteral.java

@@ -0,0 +1,39 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+
+/**
+ * Expression language AST node that represents a long integer literal.
+ * 
+ * @author Andy Clement
+ */
+public class LongLiteral extends Literal {
+
+	private final Long value;
+
+	LongLiteral(Token payload, long value) {
+		super(payload);
+		this.value = value;
+	}
+
+	@Override
+	public Long getLiteralValue() {
+		return value;
+	}
+
+}

+ 156 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/MethodReference.java

@@ -0,0 +1,156 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import java.util.List;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.AccessException;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.MethodExecutor;
+import org.springframework.expression.MethodResolver;
+import org.springframework.expression.spel.ExpressionState;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.SpelMessages;
+import org.springframework.expression.spel.internal.Utils;
+
+public class MethodReference extends SpelNode {
+
+	private final String name;
+	private MethodExecutor fastInvocationAccessor;
+
+	public MethodReference(Token payload) {
+		super(payload);
+		name = payload.getText();
+	}
+
+	@Override
+	public Object getValue(ExpressionState state) throws EvaluationException {
+		Object currentContext = state.getActiveContextObject();
+		Object[] arguments = new Object[getChildCount()];
+		for (int i = 0; i < arguments.length; i++) {
+			arguments[i] = getChild(i).getValue(state);
+		}
+		if (currentContext == null) {
+			throw new SpelException(getCharPositionInLine(), SpelMessages.ATTEMPTED_METHOD_CALL_ON_NULL_CONTEXT_OBJECT,
+					formatMethodForMessage(name, getTypes(arguments)));
+		}
+
+		if (fastInvocationAccessor != null) {
+			try {
+				return fastInvocationAccessor.execute(state.getEvaluationContext(), state.getActiveContextObject(),
+						arguments);
+			} catch (AccessException ae) {
+				// this is OK - it may have gone stale due to a class change, let's get a new one and retry before
+				// giving up
+			}
+		}
+		// either there was no accessor or it no longer existed
+		fastInvocationAccessor = findAccessorForMethod(name, getTypes(arguments), state);
+		try {
+			return fastInvocationAccessor.execute(state.getEvaluationContext(), state.getActiveContextObject(),
+					arguments);
+		} catch (AccessException ae) {
+			ae.printStackTrace();
+			throw new SpelException(getCharPositionInLine(), ae, SpelMessages.EXCEPTION_DURING_METHOD_INVOCATION, name,
+					state.getActiveContextObject().getClass().getName(), ae.getMessage());
+		}
+	}
+
+	private Class<?>[] getTypes(Object... arguments) {
+		if (arguments == null)
+			return null;
+		Class<?>[] argumentTypes = new Class[arguments.length];
+		for (int i = 0; i < arguments.length; i++) {
+			argumentTypes[i] = arguments[i].getClass();
+		}
+		return argumentTypes;
+	}
+
+	@Override
+	public String toStringAST() {
+		StringBuilder sb = new StringBuilder();
+		sb.append(name).append("(");
+		for (int i = 0; i < getChildCount(); i++) {
+			if (i > 0)
+				sb.append(",");
+			sb.append(getChild(i).toStringAST());
+		}
+		sb.append(")");
+		return sb.toString();
+	}
+
+	/**
+	 * Produce a nice string for a given method name with specified arguments.
+	 * @param name the name of the method
+	 * @param argumentTypes the types of the arguments to the method
+	 * @return nicely formatted string, eg. foo(String,int)
+	 */
+	private String formatMethodForMessage(String name, Class<?>... argumentTypes) {
+		StringBuilder sb = new StringBuilder();
+		sb.append(name);
+		sb.append("(");
+		if (argumentTypes != null) {
+			for (int i = 0; i < argumentTypes.length; i++) {
+				if (i > 0)
+					sb.append(",");
+				sb.append(argumentTypes[i].getClass());
+			}
+		}
+		sb.append(")");
+		return sb.toString();
+	}
+
+	@Override
+	public boolean isWritable(ExpressionState expressionState) throws SpelException {
+		return false;
+	}
+
+	public final MethodExecutor findAccessorForMethod(String name, Class<?>[] argumentTypes, ExpressionState state)
+			throws SpelException {
+		Object contextObject = state.getActiveContextObject();
+		EvaluationContext eContext = state.getEvaluationContext();
+		if (contextObject == null) {
+			throw new SpelException(SpelMessages.ATTEMPTED_METHOD_CALL_ON_NULL_CONTEXT_OBJECT, Utils
+					.formatMethodForMessage(name, argumentTypes));
+		}
+		List<MethodResolver> mResolvers = eContext.getMethodResolvers();
+		if (mResolvers != null) {
+			for (MethodResolver methodResolver : mResolvers) {
+				try {
+					MethodExecutor cEx = methodResolver.resolve(state.getEvaluationContext(), contextObject, name,
+							argumentTypes);
+					if (cEx != null)
+						return cEx;
+				} catch (AccessException e) {
+					Throwable cause = e.getCause();
+					if (cause instanceof SpelException) {
+						throw (SpelException) cause;
+					} else {
+						throw new SpelException(cause, SpelMessages.PROBLEM_LOCATING_METHOD, name, contextObject
+								.getClass());
+					}
+				}
+			}
+		}
+		throw new SpelException(SpelMessages.METHOD_NOT_FOUND, Utils.formatMethodForMessage(name, argumentTypes), Utils
+				.formatClassnameForMessage(contextObject instanceof Class ? ((Class<?>) contextObject) : contextObject
+						.getClass()));
+		// (contextObject instanceof Class ? ((Class<?>) contextObject).getName() : contextObject.getClass()
+		// .getName()));
+	}
+}

+ 36 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/NullLiteral.java

@@ -0,0 +1,36 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+
+public class NullLiteral extends Literal {
+
+	public NullLiteral(Token payload) {
+		super(payload);
+	}
+
+	@Override
+	public String getLiteralValue() {
+		return null;
+	}
+
+	@Override
+	public String toString() {
+		return null;
+	}
+
+}

+ 70 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/Operator.java

@@ -0,0 +1,70 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.ExpressionState;
+
+/**
+ * Common supertype for operators that operate on either one or two operands. In the case of multiply or divide there
+ * would be two operands, but for unary plus or minus, there is only one.
+ * 
+ * @author Andy Clement
+ */
+public abstract class Operator extends SpelNode {
+
+	public Operator(Token payload) {
+		super(payload);
+	}
+
+	/**
+	 * Operator expressions can never be written to
+	 */
+	@Override
+	public final boolean isWritable(ExpressionState expressionState) throws SpelException {
+		return false;
+	}
+
+	public SpelNode getLeftOperand() {
+		return getChild(0);
+	}
+
+	public SpelNode getRightOperand() {
+		return getChild(1);
+	}
+
+	public abstract String getOperatorName();
+
+	/**
+	 * String format for all operators is the same '(' [operand] [operator] [operand] ')'
+	 */
+	@Override
+	public String toStringAST() {
+		StringBuffer sb = new StringBuffer();
+		if (getChildCount() > 0)
+			sb.append("(");
+		sb.append(getChild(0).toStringAST());
+		for (int i = 1; i < getChildCount(); i++) {
+			sb.append(" ").append(getOperatorName()).append(" ");
+			sb.append(getChild(i).toStringAST());
+		}
+		if (getChildCount() > 0)
+			sb.append(")");
+		return sb.toString();
+	}
+
+}

+ 65 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorAnd.java

@@ -0,0 +1,65 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.ExpressionState;
+
+/**
+ * Represents the boolean AND operation.
+ * 
+ * @author Andy Clement
+ */
+public class OperatorAnd extends Operator {
+
+	public OperatorAnd(Token payload) {
+		super(payload);
+	}
+
+	@Override
+	public String getOperatorName() {
+		return "and";
+	}
+
+	@Override
+	public Object getValue(ExpressionState state) throws EvaluationException {
+		boolean leftValue;
+		boolean rightValue;
+
+		try {
+			leftValue = state.toBoolean(getLeftOperand().getValue(state));
+		} catch (SpelException ee) {
+			ee.setPosition(getLeftOperand().getCharPositionInLine());
+			throw ee;
+		}
+
+		if (leftValue == false) {
+			return false; // no need to evaluate right operand
+		}
+
+		try {
+			rightValue = state.toBoolean(getRightOperand().getValue(state));
+		} catch (SpelException ee) {
+			ee.setPosition(getRightOperand().getCharPositionInLine());
+			throw ee;
+		}
+
+		return /* leftValue && */rightValue;
+	}
+
+}

+ 73 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorBetween.java

@@ -0,0 +1,73 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import java.util.List;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.TypeComparator;
+import org.springframework.expression.spel.ExpressionState;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.SpelMessages;
+
+/**
+ * Represents the between operator. The left operand to between must be a single value and the right operand must be a
+ * list - this operator returns true if the left operand is between (using the registered comparator) the two elements
+ * in the list. The definition of between being inclusive follows the SQL BETWEEN definition.
+ * 
+ * @author Andy Clement
+ */
+public class OperatorBetween extends Operator {
+
+	public OperatorBetween(Token payload) {
+		super(payload);
+	}
+
+	@Override
+	public String getOperatorName() {
+		return "between";
+	}
+
+	/**
+	 * Returns a boolean based on whether a value is in the range expressed. The first operand is any value whilst the
+	 * second is a list of two values - those two values being the bounds allowed for the first operand (inclusive).
+	 * 
+	 * @param state the expression state
+	 * @return true if the left operand is in the range specified, false otherwise
+	 * @throws EvaluationException if there is a problem evaluating the expression
+	 */
+	@Override
+	public Boolean getValue(ExpressionState state) throws EvaluationException {
+		Object left = getLeftOperand().getValue(state);
+		Object right = getRightOperand().getValue(state);
+		if (!(right instanceof List) || ((List<?>) right).size() != 2) {
+			throw new SpelException(getRightOperand().getCharPositionInLine(),
+					SpelMessages.BETWEEN_RIGHT_OPERAND_MUST_BE_TWO_ELEMENT_LIST);
+		}
+		List<?> l = (List<?>) right;
+		Object low = l.get(0);
+		Object high = l.get(1);
+		TypeComparator comparator = state.getTypeComparator();
+		try {
+			return (comparator.compare(left, low) >= 0 && comparator.compare(left, high) <= 0);
+		} catch (SpelException ex) {
+			ex.setPosition(getCharPositionInLine());
+			throw ex;
+		}
+	}
+
+}

+ 63 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorDivide.java

@@ -0,0 +1,63 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.Operation;
+import org.springframework.expression.spel.ExpressionState;
+
+/**
+ * Implements division operator
+ * 
+ * @author Andy Clement
+ */
+public class OperatorDivide extends Operator {
+
+	public OperatorDivide(Token payload) {
+		super(payload);
+	}
+
+	@Override
+	public String getOperatorName() {
+		return "/";
+	}
+
+	@Override
+	public Object getValue(ExpressionState state) throws EvaluationException {
+		Object operandOne = getLeftOperand().getValue(state);
+		Object operandTwo = getRightOperand().getValue(state);
+		if (operandOne instanceof Number && operandTwo instanceof Number) {
+			Number op1 = (Number) operandOne;
+			Number op2 = (Number) operandTwo;
+			if (op1 instanceof Double || op2 instanceof Double) {
+				Double result = op1.doubleValue() / op2.doubleValue();
+				return result;
+			} else if (op1 instanceof Float || op2 instanceof Float) {
+				Float result = op1.floatValue() / op2.floatValue();
+				return result;
+			} else if (op1 instanceof Long || op2 instanceof Long) {
+				Long result = op1.longValue() / op2.longValue();
+				return result;
+			} else { // TODO what about non-int result of the division?
+				Integer result = op1.intValue() / op2.intValue();
+				return result;
+			}
+		}
+		return state.operate(Operation.DIVIDE, operandOne, operandTwo);
+	}
+
+}

+ 58 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorEquality.java

@@ -0,0 +1,58 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.spel.ExpressionState;
+
+/**
+ * Implements equality operator.
+ * 
+ * @author Andy Clement
+ */
+public class OperatorEquality extends Operator {
+
+	public OperatorEquality(Token payload) {
+		super(payload);
+	}
+
+	@Override
+	public String getOperatorName() {
+		return "==";
+	}
+
+	@Override
+	public Object getValue(ExpressionState state) throws EvaluationException {
+		Object left = getLeftOperand().getValue(state);
+		Object right = getRightOperand().getValue(state);
+		if (left instanceof Number && right instanceof Number) {
+			Number op1 = (Number) left;
+			Number op2 = (Number) right;
+			if (op1 instanceof Double || op2 instanceof Double) {
+				return op1.doubleValue() == op2.doubleValue();
+			} else if (op1 instanceof Float || op2 instanceof Float) {
+				return op1.floatValue() == op2.floatValue();
+			} else if (op1 instanceof Long || op2 instanceof Long) {
+				return op1.longValue() == op2.longValue();
+			} else {
+				return op1.intValue() == op2.intValue();
+			}
+		}
+		return state.getTypeComparator().compare(left, right) == 0;
+	}
+
+}

+ 58 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorGreaterThan.java

@@ -0,0 +1,58 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.spel.ExpressionState;
+
+/**
+ * Implements greater than operator.
+ * 
+ * @author Andy Clement
+ */
+public class OperatorGreaterThan extends Operator {
+
+	public OperatorGreaterThan(Token payload) {
+		super(payload);
+	}
+
+	@Override
+	public String getOperatorName() {
+		return ">";
+	}
+
+	@Override
+	public Object getValue(ExpressionState state) throws EvaluationException {
+		Object left = getLeftOperand().getValue(state);
+		Object right = getRightOperand().getValue(state);
+		if (left instanceof Number && right instanceof Number) {
+			Number op1 = (Number) left;
+			Number op2 = (Number) right;
+			if (op1 instanceof Double || op2 instanceof Double) {
+				return op1.doubleValue() > op2.doubleValue();
+			} else if (op1 instanceof Float || op2 instanceof Float) {
+				return op1.floatValue() > op2.floatValue();
+			} else if (op1 instanceof Long || op2 instanceof Long) {
+				return op1.longValue() > op2.longValue();
+			} else {
+				return op1.intValue() > op2.intValue();
+			}
+		}
+		return state.getTypeComparator().compare(left, right) > 0;
+	}
+
+}

+ 58 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorGreaterThanOrEqual.java

@@ -0,0 +1,58 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.spel.ExpressionState;
+
+/**
+ * Implements greater than or equal operator.
+ * 
+ * @author Andy Clement
+ */
+public class OperatorGreaterThanOrEqual extends Operator {
+
+	public OperatorGreaterThanOrEqual(Token payload) {
+		super(payload);
+	}
+
+	@Override
+	public String getOperatorName() {
+		return ">=";
+	}
+
+	@Override
+	public Object getValue(ExpressionState state) throws EvaluationException {
+		Object left = getLeftOperand().getValue(state);
+		Object right = getRightOperand().getValue(state);
+		if (left instanceof Number && right instanceof Number) {
+			Number op1 = (Number) left;
+			Number op2 = (Number) right;
+			if (op1 instanceof Double || op2 instanceof Double) {
+				return op1.doubleValue() >= op2.doubleValue();
+			} else if (op1 instanceof Float || op2 instanceof Float) {
+				return op1.floatValue() >= op2.floatValue();
+			} else if (op1 instanceof Long || op2 instanceof Long) {
+				return op1.longValue() >= op2.longValue();
+			} else {
+				return op1.intValue() >= op2.intValue();
+			}
+		}
+		return state.getTypeComparator().compare(left, right) >= 0;
+	}
+
+}

+ 58 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorInequality.java

@@ -0,0 +1,58 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.spel.ExpressionState;
+
+/**
+ * Implements the not-equal operator
+ * 
+ * @author Andy Clement
+ */
+public class OperatorInequality extends Operator {
+
+	public OperatorInequality(Token payload) {
+		super(payload);
+	}
+
+	@Override
+	public String getOperatorName() {
+		return "!=";
+	}
+
+	@Override
+	public Object getValue(ExpressionState state) throws EvaluationException {
+		Object left = getLeftOperand().getValue(state);
+		Object right = getRightOperand().getValue(state);
+		if (left instanceof Number && right instanceof Number) {
+			Number op1 = (Number) left;
+			Number op2 = (Number) right;
+			if (op1 instanceof Double || op2 instanceof Double) {
+				return op1.doubleValue() != op2.doubleValue();
+			} else if (op1 instanceof Float || op2 instanceof Float) {
+				return op1.floatValue() != op2.floatValue();
+			} else if (op1 instanceof Long || op2 instanceof Long) {
+				return op1.longValue() != op2.longValue();
+			} else {
+				return op1.intValue() != op2.intValue();
+			}
+		}
+		return state.getTypeComparator().compare(left, right) != 0;
+	}
+
+}

+ 64 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorInstanceof.java

@@ -0,0 +1,64 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.spel.ExpressionState;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.SpelMessages;
+
+/**
+ * The operator 'instanceof' checks if an object is of the class specified in the right hand operand, in the same way
+ * that instanceof does in Java.
+ * 
+ * @author Andy Clement
+ */
+public class OperatorInstanceof extends Operator {
+
+	public OperatorInstanceof(Token payload) {
+		super(payload);
+	}
+
+	@Override
+	public String getOperatorName() {
+		return "instanceof";
+	}
+
+	/**
+	 * Compare the left operand to see it is an instance of the type specified as the right operand. The right operand
+	 * must be a class.
+	 * 
+	 * @param state the expression state
+	 * @return true if the left operand is an instanceof of the right operand, otherwise false
+	 * @throws EvaluationException if there is a problem evaluating the expression
+	 */
+	@Override
+	public Boolean getValue(ExpressionState state) throws EvaluationException {
+		Object left = getLeftOperand().getValue(state);
+		Object right = getRightOperand().getValue(state);
+		if (left == null) {
+			return false; // null is not an instanceof anything
+		}
+		if (right == null || !(right instanceof Class<?>)) {
+			throw new SpelException(getRightOperand().getCharPositionInLine(),
+					SpelMessages.INSTANCEOF_OPERATOR_NEEDS_CLASS_OPERAND, (right == null ? "null" : right.getClass().getName()));
+		}
+		Class<?> rightClass = (Class<?>) right;
+		return rightClass.isAssignableFrom(left.getClass());
+	}
+
+}

+ 58 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorLessThan.java

@@ -0,0 +1,58 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.spel.ExpressionState;
+
+/**
+ * Implements the less than operator
+ * 
+ * @author Andy Clement
+ */
+public class OperatorLessThan extends Operator {
+
+	public OperatorLessThan(Token payload) {
+		super(payload);
+	}
+
+	@Override
+	public String getOperatorName() {
+		return "<";
+	}
+
+	@Override
+	public Object getValue(ExpressionState state) throws EvaluationException {
+		Object left = getLeftOperand().getValue(state);
+		Object right = getRightOperand().getValue(state);
+		if (left instanceof Number && right instanceof Number) {
+			Number op1 = (Number) left;
+			Number op2 = (Number) right;
+			if (op1 instanceof Double || op2 instanceof Double) {
+				return op1.doubleValue() < op2.doubleValue();
+			} else if (op1 instanceof Float || op2 instanceof Float) {
+				return op1.floatValue() < op2.floatValue();
+			} else if (op1 instanceof Long || op2 instanceof Long) {
+				return op1.longValue() < op2.longValue();
+			} else {
+				return op1.intValue() < op2.intValue();
+			}
+		}
+		return state.getTypeComparator().compare(left, right) < 0;
+	}
+
+}

+ 58 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorLessThanOrEqual.java

@@ -0,0 +1,58 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.spel.ExpressionState;
+
+/**
+ * Implements the less than or equal operator
+ * 
+ * @author Andy Clement
+ */
+public class OperatorLessThanOrEqual extends Operator {
+
+	public OperatorLessThanOrEqual(Token payload) {
+		super(payload);
+	}
+
+	@Override
+	public Object getValue(ExpressionState state) throws EvaluationException {
+		Object left = getLeftOperand().getValue(state);
+		Object right = getRightOperand().getValue(state);
+		if (left instanceof Number && right instanceof Number) {
+			Number op1 = (Number) left;
+			Number op2 = (Number) right;
+			if (op1 instanceof Double || op2 instanceof Double) {
+				return op1.doubleValue() <= op2.doubleValue();
+			} else if (op1 instanceof Float || op2 instanceof Float) {
+				return op1.floatValue() <= op2.floatValue();
+			} else if (op1 instanceof Long || op2 instanceof Long) {
+				return op1.longValue() <= op2.longValue();
+			} else {
+				return op1.intValue() <= op2.intValue();
+			}
+		}
+		return state.getTypeComparator().compare(left, right) <= 0;
+	}
+
+	@Override
+	public String getOperatorName() {
+		return "<=";
+	}
+
+}

+ 75 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorMatches.java

@@ -0,0 +1,75 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.spel.ExpressionState;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.SpelMessages;
+
+/**
+ * Implements the matches operator. Matches takes two operands. The first is a string and the second is a java regex. It
+ * will return true when getValue() is called if the first operand matches the regex.
+ * 
+ * @author Andy Clement
+ */
+public class OperatorMatches extends Operator {
+
+	public OperatorMatches(Token payload) {
+		super(payload);
+	}
+
+	@Override
+	public String getOperatorName() {
+		return "matches";
+	}
+
+	/**
+	 * Check the first operand matches the regex specified as the second operand.
+	 * 
+	 * @param state the expression state
+	 * @return true if the first operand matches the regex specified as the second operand, otherwise false
+	 * @throws EvaluationException if there is a problem evaluating the expression (e.g. the regex is invalid)
+	 */
+	@Override
+	public Boolean getValue(ExpressionState state) throws EvaluationException {
+		SpelNode leftOp = getLeftOperand();
+		SpelNode rightOp = getRightOperand();
+		Object left = leftOp.getValue(state, String.class);
+		Object right = getRightOperand().getValue(state);
+		try {
+			if (!(left instanceof String)) {
+				throw new SpelException(leftOp.getCharPositionInLine(),
+						SpelMessages.INVALID_FIRST_OPERAND_FOR_MATCHES_OPERATOR, left);
+			}
+			if (!(right instanceof String)) {
+				throw new SpelException(rightOp.getCharPositionInLine(),
+						SpelMessages.INVALID_SECOND_OPERAND_FOR_MATCHES_OPERATOR, right);
+			}
+			Pattern pattern = Pattern.compile((String) right);
+			Matcher matcher = pattern.matcher((String) left);
+			return matcher.matches();
+		} catch (PatternSyntaxException pse) {
+			throw new SpelException(rightOp.getCharPositionInLine(), pse, SpelMessages.INVALID_PATTERN, right);
+		}
+	}
+
+}

+ 95 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorMinus.java

@@ -0,0 +1,95 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.Operation;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.SpelMessages;
+import org.springframework.expression.spel.ExpressionState;
+
+/**
+ * Implements the minus operator. If there is only one operand it is a unary minus.
+ * 
+ * @author Andy Clement
+ */
+public class OperatorMinus extends Operator {
+
+	public OperatorMinus(Token payload) {
+		super(payload);
+	}
+
+	@Override
+	public String getOperatorName() {
+		return "-";
+	}
+
+	@Override
+	public String toStringAST() {
+		if (getRightOperand() == null) { // unary minus
+			return new StringBuilder().append("-").append(getLeftOperand()).toString();
+		}
+		return super.toStringAST();
+	}
+
+	@Override
+	public Object getValue(ExpressionState state) throws EvaluationException {
+		SpelNode leftOp = getLeftOperand();
+		SpelNode rightOp = getRightOperand();
+		if (rightOp == null) {// If only one operand, then this is unary minus
+			Object left = leftOp.getValue(state);
+			if (left instanceof Number) {
+				Number n = (Number) left;
+				if (left instanceof Double) {
+					Double result = 0 - n.doubleValue();
+					return result;
+				} else if (left instanceof Float) {
+					Float result = 0 - n.floatValue();
+					return result;
+				} else if (left instanceof Long) {
+					Long result = 0 - n.longValue();
+					return result;
+				} else {
+					Integer result = 0 - n.intValue();
+					return result;
+				}
+			}
+			throw new SpelException(SpelMessages.CANNOT_NEGATE_TYPE, left.getClass().getName());
+		} else {
+			Object left = leftOp.getValue(state);
+			Object right = rightOp.getValue(state);
+			if (left instanceof Number && right instanceof Number) {
+				Number op1 = (Number) left;
+				Number op2 = (Number) right;
+				if (op1 instanceof Double || op2 instanceof Double) {
+					Double result = op1.doubleValue() - op2.doubleValue();
+					return result;
+				} else if (op1 instanceof Float || op2 instanceof Float) {
+					Float result = op1.floatValue() - op2.floatValue();
+					return result;
+				} else if (op1 instanceof Long || op2 instanceof Long) {
+					Long result = op1.longValue() - op2.longValue();
+					return result;
+				} else {
+					Integer result = op1.intValue() - op2.intValue();
+					return result;
+				}
+			}
+			return state.operate(Operation.SUBTRACT, left, right);
+		}
+	}
+}

+ 63 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorModulus.java

@@ -0,0 +1,63 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.Operation;
+import org.springframework.expression.spel.ExpressionState;
+
+/**
+ * Implements the modulus operator.
+ * 
+ * @author Andy Clement
+ */
+public class OperatorModulus extends Operator {
+
+	public OperatorModulus(Token payload) {
+		super(payload);
+	}
+
+	@Override
+	public String getOperatorName() {
+		return "%";
+	}
+
+	@Override
+	public Object getValue(ExpressionState state) throws EvaluationException {
+		Object operandOne = getLeftOperand().getValue(state);
+		Object operandTwo = getRightOperand().getValue(state);
+		if (operandOne instanceof Number && operandTwo instanceof Number) {
+			Number op1 = (Number) operandOne;
+			Number op2 = (Number) operandTwo;
+			if (op1 instanceof Double || op2 instanceof Double) {
+				Double result = op1.doubleValue() % op2.doubleValue();
+				return result;
+			} else if (op1 instanceof Float || op2 instanceof Float) {
+				Float result = op1.floatValue() % op2.floatValue();
+				return result;
+			} else if (op1 instanceof Long || op2 instanceof Long) {
+				Long result = op1.longValue() % op2.longValue();
+				return result;
+			} else {
+				Integer result = op1.intValue() % op2.intValue();
+				return result;
+			}
+		}
+		return state.operate(Operation.MODULUS, operandOne, operandTwo);
+	}
+
+}

+ 86 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorMultiply.java

@@ -0,0 +1,86 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.Operation;
+import org.springframework.expression.spel.ExpressionState;
+
+/**
+ * Implements the multiply operator. Conversions and promotions:
+ * http://java.sun.com/docs/books/jls/third_edition/html/conversions.html Section 5.6.2:
+ * 
+ * If any of the operands is of a reference type, unboxing conversion (¤5.1.8) is performed. Then:<br>
+ * If either operand is of type double, the other is converted to double.<br>
+ * Otherwise, if either operand is of type float, the other is converted to float.<br>
+ * Otherwise, if either operand is of type long, the other is converted to long.<br>
+ * Otherwise, both operands are converted to type int.
+ * 
+ * @author Andy Clement
+ */
+public class OperatorMultiply extends Operator {
+
+	public OperatorMultiply(Token payload) {
+		super(payload);
+	}
+
+	/**
+	 * Implements multiply directly here for some types of operand, otherwise delegates to any registered overloader for
+	 * types it does not recognize. Supported types here are:
+	 * <ul>
+	 * <li>integers
+	 * <li>doubles
+	 * <li>string and int ('abc' * 2 == 'abcabc')
+	 * </ul>
+	 */
+	@Override
+	public Object getValue(ExpressionState state) throws EvaluationException {
+		Object operandOne = getLeftOperand().getValue(state);
+		Object operandTwo = getRightOperand().getValue(state);
+		if (operandOne instanceof Number && operandTwo instanceof Number) {
+			Number op1 = (Number) operandOne;
+			Number op2 = (Number) operandTwo;
+			if (op1 instanceof Double || op2 instanceof Double) {
+				Double result = op1.doubleValue() * op2.doubleValue();
+				return result;
+			} else if (op1 instanceof Float || op2 instanceof Float) {
+				Float result = op1.floatValue() * op2.floatValue();
+				return result;
+			} else if (op1 instanceof Long || op2 instanceof Long) {
+				Long result = op1.longValue() * op2.longValue();
+				return result;
+			} else { // promote to int
+				Integer result = op1.intValue() * op2.intValue();
+				return result;
+			}
+		} else if (operandOne instanceof String && operandTwo instanceof Integer) {
+			int repeats = ((Integer) operandTwo).intValue();
+			StringBuilder result = new StringBuilder();
+			for (int i = 0; i < repeats; i++) {
+				result.append(operandOne);
+			}
+			return result.toString();
+		}
+		return state.operate(Operation.MULTIPLY, operandOne, operandTwo);
+	}
+
+	@Override
+	public String getOperatorName() {
+		return "*";
+	}
+
+}

+ 51 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorNot.java

@@ -0,0 +1,51 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.ExpressionState;
+
+public class OperatorNot extends SpelNode { // Not is a unary operator so do not extend BinaryOperator
+
+	public OperatorNot(Token payload) {
+		super(payload);
+	}
+
+	@Override
+	public Object getValue(ExpressionState state) throws EvaluationException {
+		try {
+			boolean value = state.toBoolean(getChild(0).getValue(state));
+			return !value;
+		} catch (SpelException see) {
+			see.setPosition(getChild(0).getCharPositionInLine());
+			throw see;
+		}
+	}
+
+	@Override
+	public String toStringAST() {
+		StringBuilder sb = new StringBuilder();
+		sb.append("!").append(getChild(0).toStringAST());
+		return sb.toString();
+	}
+
+	@Override
+	public boolean isWritable(ExpressionState expressionState) throws SpelException {
+		return false;
+	}
+}

+ 63 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorOr.java

@@ -0,0 +1,63 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.ExpressionState;
+
+/**
+ * Represents the boolean OR operation.
+ * 
+ * @author Andy Clement
+ */
+public class OperatorOr extends Operator {
+
+	public OperatorOr(Token payload) {
+		super(payload);
+	}
+
+	@Override
+	public String getOperatorName() {
+		return "or";
+	}
+
+	@Override
+	public Object getValue(ExpressionState state) throws EvaluationException {
+		boolean leftValue;
+		boolean rightValue;
+		try {
+			leftValue = state.toBoolean(getLeftOperand().getValue(state));
+		} catch (SpelException see) {
+			see.setPosition(getLeftOperand().getCharPositionInLine());
+			throw see;
+		}
+
+		if (leftValue == true)
+			return true; // no need to evaluate right operand
+
+		try {
+			rightValue = state.toBoolean(getRightOperand().getValue(state));
+		} catch (SpelException see) {
+			see.setPosition(getRightOperand().getCharPositionInLine());
+			throw see;
+		}
+
+		return leftValue || rightValue;
+	}
+
+}

+ 88 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/OperatorPlus.java

@@ -0,0 +1,88 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.Operation;
+import org.springframework.expression.spel.ExpressionState;
+
+public class OperatorPlus extends Operator {
+
+	public OperatorPlus(Token payload) {
+		super(payload);
+	}
+
+	@Override
+	public Object getValue(ExpressionState state) throws EvaluationException {
+		SpelNode leftOp = getLeftOperand();
+		SpelNode rightOp = getRightOperand();
+		if (rightOp == null) { // If only one operand, then this is unary plus
+			Object operandOne = leftOp.getValue(state);
+			if (operandOne instanceof Number) {
+				return new Integer(((Number) operandOne).intValue());
+			}
+			return state.operate(Operation.ADD, operandOne, null);
+		} else {
+			Object operandOne = leftOp.getValue(state);
+			Object operandTwo = rightOp.getValue(state);
+			if (operandOne instanceof Number && operandTwo instanceof Number) {
+				Number op1 = (Number) operandOne;
+				Number op2 = (Number) operandTwo;
+				if (op1 instanceof Double || op2 instanceof Double) {
+					Double result = op1.doubleValue() + op2.doubleValue();
+					return result;
+				} else if (op1 instanceof Float || op2 instanceof Float) {
+					Float result = op1.floatValue() + op2.floatValue();
+					return result;
+				} else if (op1 instanceof Long || op2 instanceof Long) {
+					Long result = op1.longValue() + op2.longValue();
+					return result;
+				} else { // TODO what about overflow?
+					Integer result = op1.intValue() + op2.intValue();
+					return result;
+				}
+			} else if (operandOne instanceof String && operandTwo instanceof String) {
+				return new StringBuilder((String) operandOne).append((String) operandTwo).toString();
+			} else if (operandOne instanceof String && operandTwo instanceof Integer) {
+				String l = (String) operandOne;
+				Integer i = (Integer) operandTwo;
+
+				// implements character + int (ie. a + 1 = b)
+				if (l.length() == 1) {
+					Character c = new Character((char) (new Character(l.charAt(0)) + i));
+					return c.toString();
+				}
+
+				return new StringBuilder((String) operandOne).append(((Integer) operandTwo).toString()).toString();
+			}
+			return state.operate(Operation.ADD, operandOne, operandTwo);
+		}
+	}
+
+	@Override
+	public String getOperatorName() {
+		return "+";
+	}
+
+	@Override
+	public String toStringAST() {
+		if (getRightOperand() == null) { // unary plus
+			return new StringBuilder().append("+").append(getLeftOperand()).toString();
+		}
+		return super.toStringAST();
+	}
+}

+ 46 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/Placeholder.java

@@ -0,0 +1,46 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.SpelMessages;
+import org.springframework.expression.spel.ExpressionState;
+
+/**
+ * PlaceHolder nodes are created for tokens that come through from the grammar purely to give us additional positional
+ * information for messages/etc.
+ * 
+ * @author Andy Clement
+ * 
+ */
+public class Placeholder extends SpelNode {
+
+	public Placeholder(Token payload) {
+		super(payload);
+	}
+
+	@Override
+	public String getValue(ExpressionState state) throws SpelException {
+		throw new SpelException(getCharPositionInLine(), SpelMessages.PLACEHOLDER_SHOULD_NEVER_BE_EVALUATED);
+	}
+
+	@Override
+	public String toStringAST() {
+		return getText();
+	}
+
+}

+ 98 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/Projection.java

@@ -0,0 +1,98 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.SpelMessages;
+import org.springframework.expression.spel.ExpressionState;
+import org.springframework.expression.spel.internal.KeyValuePair;
+
+/**
+ * Represents projection, where a given operation is performed on all elements in some input sequence, returning 
+ * a new sequence of the same size. For example:
+ * "{1,2,3,4,5,6,7,8,9,10}.!{#isEven(#this)}" returns "[n, y, n, y, n, y, n, y, n, y]"
+ * 
+ * @author Andy Clement
+ * 
+ */
+public class Projection extends SpelNode {
+
+	public Projection(Token payload) {
+		super(payload);
+	}
+
+	@Override
+	public Object getValue(ExpressionState state) throws EvaluationException {
+		Object operand = state.getActiveContextObject();
+
+		// When the input is a map, we push a special context object on the stack
+		// before calling the specified operation. This special context object
+		// has two fields 'key' and 'value' that refer to the map entries key
+		// and value, and they can be referenced in the operation
+		// eg. {'a':'y','b':'n'}.!{value=='y'?key:null}" == ['a', null]
+		if (operand instanceof Map) {
+			Map<?, ?> mapdata = (Map<?, ?>) operand;
+			List<Object> result = new ArrayList<Object>();
+			for (Object k : mapdata.keySet()) {
+				try {
+					state.pushActiveContextObject(new KeyValuePair(k, mapdata.get(k)));
+					result.add(getChild(0).getValue(state));
+				} finally {
+					state.popActiveContextObject();
+				}
+			}
+			return result;
+		} else if (operand instanceof Collection) {
+			List<Object> data = new ArrayList<Object>();
+			data.addAll((Collection<?>) operand);
+			List<Object> result = new ArrayList<Object>();
+			int idx = 0;
+			for (Object element : data) {
+				try {
+					state.pushActiveContextObject(element);
+					state.enterScope("index", idx);
+					result.add(getChild(0).getValue(state));
+				} finally {
+					state.exitScope();
+					state.popActiveContextObject();
+				}
+				idx++;
+			}
+			return result;
+		} else {
+			throw new SpelException(SpelMessages.PROJECTION_NOT_SUPPORTED_ON_TYPE, operand.getClass().getName());
+		}
+	}
+
+	@Override
+	public String toStringAST() {
+		StringBuffer sb = new StringBuffer();
+		return sb.append("!{").append(getChild(0).toStringAST()).append("}").toString();
+	}
+
+	@Override
+	public boolean isWritable(ExpressionState expressionState) throws SpelException {
+		return false;
+	}
+
+}

+ 245 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/PropertyOrFieldReference.java

@@ -0,0 +1,245 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.AccessException;
+import org.springframework.expression.CacheablePropertyAccessor;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.PropertyAccessor;
+import org.springframework.expression.PropertyReaderExecutor;
+import org.springframework.expression.PropertyWriterExecutor;
+import org.springframework.expression.spel.ExpressionState;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.SpelMessages;
+import org.springframework.expression.spel.internal.Utils;
+
+/**
+ * Represents a simple property or field reference.
+ * 
+ * @author Andy Clement
+ */
+public class PropertyOrFieldReference extends SpelNode {
+
+	public static boolean useCaching = true;
+
+	private final Object name;
+	private PropertyReaderExecutor cachedReaderExecutor;
+	private PropertyWriterExecutor cachedWriterExecutor;
+
+	public PropertyOrFieldReference(Token payload) {
+		super(payload);
+		name = payload.getText();
+	}
+
+	@Override
+	public Object getValue(ExpressionState state) throws SpelException {
+		return readProperty(state, name);
+	}
+
+	@Override
+	public void setValue(ExpressionState state, Object newValue) throws SpelException {
+		writeProperty(state, name, newValue);
+	}
+
+	@Override
+	public boolean isWritable(ExpressionState state) throws SpelException {
+		return isWritableProperty(name, state);
+	}
+
+	@Override
+	public String toStringAST() {
+		return name.toString();
+	}
+
+	/**
+	 * Attempt to read the named property from the current context object.
+	 * 
+	 * @param state the evaluation state
+	 * @param name the name of the property
+	 * @return the value of the property
+	 * @throws SpelException if any problem accessing the property or it cannot be found
+	 */
+	private Object readProperty(ExpressionState state, Object name) throws SpelException {
+		Object contextObject = state.getActiveContextObject();
+		EvaluationContext eContext = state.getEvaluationContext();
+
+		if (cachedReaderExecutor != null) {
+			try {
+				return cachedReaderExecutor.execute(state.getEvaluationContext(), contextObject);
+			} catch (AccessException ae) {
+				// this is OK - it may have gone stale due to a class change,
+				// let's try to get a new one and call it before giving up
+			}
+		}
+
+		Class<?> contextObjectClass = getObjectClass(contextObject);
+
+		List<PropertyAccessor> accessorsToTry = getPropertyAccessorsToTry(contextObjectClass, state);
+
+		// Go through the accessors that may be able to resolve it. If they are a cacheable accessor then
+		// get the accessor and use it. If they are not cacheable but report they can read the property
+		// then ask them to read it
+		if (accessorsToTry != null) {
+			try {
+				for (PropertyAccessor accessor : accessorsToTry) {
+					if (accessor instanceof CacheablePropertyAccessor) {
+						cachedReaderExecutor = ((CacheablePropertyAccessor) accessor).getReaderAccessor(eContext,
+								contextObject, name);
+						if (cachedReaderExecutor != null) {
+							try {
+								return cachedReaderExecutor.execute(state.getEvaluationContext(), contextObject);
+							} catch (AccessException ae) {
+								cachedReaderExecutor = null;
+								throw ae;
+							} finally {
+								if (!useCaching) {
+									cachedReaderExecutor = null;
+								}
+							}
+						}
+					} else {
+						if (accessor.canRead(eContext, contextObject, name)) {
+							Object value = accessor.read(eContext, contextObject, name);
+							return value;
+						}
+					}
+				}
+			} catch (AccessException ae) {
+				throw new SpelException(ae, SpelMessages.EXCEPTION_DURING_PROPERTY_READ, name, ae.getMessage());
+			}
+		}
+		throw new SpelException(SpelMessages.PROPERTY_OR_FIELD_NOT_FOUND, name, Utils
+				.formatClassnameForMessage(contextObjectClass));
+	}
+
+	private void writeProperty(ExpressionState state, Object name, Object newValue) throws SpelException {
+		Object contextObject = state.getActiveContextObject();
+		EvaluationContext eContext = state.getEvaluationContext();
+
+		if (cachedWriterExecutor != null) {
+			try {
+				cachedWriterExecutor.execute(state.getEvaluationContext(), contextObject, newValue);
+				return;
+			} catch (AccessException ae) {
+				// this is OK - it may have gone stale due to a class change,
+				// let's try to get a new one and call it before giving up
+			}
+		}
+
+		Class<?> contextObjectClass = getObjectClass(contextObject);
+
+		List<PropertyAccessor> accessorsToTry = getPropertyAccessorsToTry(contextObjectClass, state);
+		if (accessorsToTry != null) {
+			try {
+				for (PropertyAccessor accessor : accessorsToTry) {
+					if (accessor instanceof CacheablePropertyAccessor) {
+						cachedWriterExecutor = ((CacheablePropertyAccessor) accessor).getWriterAccessor(eContext,
+								contextObject, name);
+						if (cachedWriterExecutor != null) {
+							try {
+								cachedWriterExecutor.execute(state.getEvaluationContext(), contextObject, newValue);
+								return;
+							} catch (AccessException ae) {
+								cachedWriterExecutor = null;
+								throw ae;
+							} finally {
+								if (!useCaching) {
+									cachedWriterExecutor = null;
+								}
+							}
+						}
+					} else {
+						if (accessor.canWrite(eContext, contextObject, name)) {
+							accessor.write(eContext, contextObject, name, newValue);
+							return;
+						}
+					}
+				}
+			} catch (AccessException ae) {
+				throw new SpelException(getCharPositionInLine(), ae, SpelMessages.EXCEPTION_DURING_PROPERTY_WRITE,
+						name, ae.getMessage());
+			}
+		}
+		throw new SpelException(SpelMessages.PROPERTY_OR_FIELD_SETTER_NOT_FOUND, name, Utils
+				.formatClassnameForMessage(contextObjectClass));
+	}
+
+	public boolean isWritableProperty(Object name, ExpressionState state) throws SpelException {
+		Object contextObject = state.getActiveContextObject();
+		EvaluationContext eContext = state.getEvaluationContext();
+		if (contextObject == null) {
+			throw new SpelException(SpelMessages.ATTEMPTED_PROPERTY_FIELD_REF_ON_NULL_CONTEXT_OBJECT, name);
+		}
+		List<PropertyAccessor> resolversToTry = getPropertyAccessorsToTry(
+				(contextObject instanceof Class) ? ((Class<?>) contextObject) : contextObject.getClass(), state);
+		if (resolversToTry != null) {
+			for (PropertyAccessor pfResolver : resolversToTry) {
+				try {
+					if (pfResolver.canWrite(eContext, contextObject, name)) {
+						return true;
+					}
+				} catch (AccessException ae) {
+					// let others try
+				}
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Determines the set of property resolvers that should be used to try and access a property on the specified target
+	 * type. The resolvers are considered to be in an ordered list, however in the returned list any that are exact
+	 * matches for the input target type (as opposed to 'general' resolvers that could work for any type) are placed at
+	 * the start of the list. In addition, there are specific resolvers that exactly name the class in question and
+	 * resolvers that name a specific class but it is a supertype of the class we have. These are put at the end of the
+	 * specific resolvers set and will be tried after exactly matching accessors but before generic accessors.
+	 * 
+	 * @param targetType the type upon which property access is being attempted
+	 * @return a list of resolvers that should be tried in order to access the property
+	 */
+	private List<PropertyAccessor> getPropertyAccessorsToTry(Class<?> targetType, ExpressionState state) {
+		List<PropertyAccessor> specificAccessors = new ArrayList<PropertyAccessor>();
+		List<PropertyAccessor> generalAccessors = new ArrayList<PropertyAccessor>();
+		for (PropertyAccessor resolver : state.getPropertyAccessors()) {
+			Class<?>[] targets = resolver.getSpecificTargetClasses();
+			if (targets == null) { // generic resolver that says it can be used for any type
+				generalAccessors.add(resolver);
+			} else {
+				if (targetType != null) {
+					int pos = 0;
+					for (int i = 0; i < targets.length; i++) {
+						Class<?> clazz = targets[i];
+						if (clazz == targetType) { // put exact matches on the front to be tried first?
+							specificAccessors.add(pos++, resolver);
+						} else if (clazz.isAssignableFrom(targetType)) { // put supertype matches at the end of the
+																			// specificAccessor list
+							generalAccessors.add(resolver);
+						}
+					}
+				}
+			}
+		}
+		List<PropertyAccessor> resolvers = new ArrayList<PropertyAccessor>();
+		resolvers.addAll(specificAccessors);
+		resolvers.addAll(generalAccessors);
+		return resolvers;
+	}
+
+}

+ 69 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/QualifiedIdentifier.java

@@ -0,0 +1,69 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.spel.ExpressionState;
+
+/**
+ * Represents a dot separated sequence of strings that indicate a package qualified type reference.
+ * <p>
+ * Example: "java.lang.String" as in the expression "new java.lang.String('hello')"
+ * 
+ * @author Andy Clement
+ * 
+ */
+public class QualifiedIdentifier extends SpelNode {
+
+	private String value;
+
+	public QualifiedIdentifier(Token payload) {
+		super(payload);
+		// value = payload.getText();
+	}
+
+	@Override
+	public Object getValue(ExpressionState state) throws EvaluationException {
+		// Cache the concatenation of child identifiers
+		if (value == null) {
+			StringBuilder sb = new StringBuilder();
+			for (int i = 0; i < getChildCount(); i++) {
+				if (i > 0)
+					sb.append(".");
+				sb.append(getChild(i).getValue(state));
+			}
+			value = sb.toString();
+		}
+		return value;
+	}
+
+	@Override
+	public String toStringAST() {
+		StringBuilder sb = new StringBuilder();
+		if (value != null) {
+			sb.append(value);
+		} else {
+			for (int i = 0; i < getChildCount(); i++) {
+				if (i > 0)
+					sb.append(".");
+				sb.append(getChild(i).toStringAST());
+			}
+		}
+		return sb.toString();
+	}
+
+}

+ 34 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/RealLiteral.java

@@ -0,0 +1,34 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+
+public class RealLiteral extends Literal {
+
+	private final Double value;
+
+	public RealLiteral(Token payload) {
+		super(payload);
+		value = Double.parseDouble(payload.getText());
+	}
+
+	@Override
+	public Double getLiteralValue() {
+		return value;
+	}
+
+}

+ 146 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/Selection.java

@@ -0,0 +1,146 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.SpelMessages;
+import org.springframework.expression.spel.ExpressionState;
+import org.springframework.expression.spel.internal.KeyValuePair;
+
+/**
+ * Represents selection over a map or collection. For example: {1,2,3,4,5,6,7,8,9,10}.?{#isEven(#this) == 'y'} returns
+ * [2, 4, 6, 8, 10]
+ * 
+ * Basically a subset of the input data is returned based on the evaluation of the expression supplied as selection
+ * criteria.
+ * 
+ * @author Andy Clement
+ */
+public class Selection extends SpelNode {
+
+	public final static int ALL = 0; // ?{}
+	public final static int FIRST = 1; // ^{}
+	public final static int LAST = 2; // ${}
+
+	private final int variant;
+
+	public Selection(Token payload, int variant) {
+		super(payload);
+		this.variant = variant;
+	}
+
+	@Override
+	public Object getValue(ExpressionState state) throws EvaluationException {
+		Object operand = state.getActiveContextObject();
+		SpelNode selectionCriteria = getChild(0);
+		if (operand instanceof Map) {
+			Map<?, ?> mapdata = (Map<?, ?>) operand;
+			List<Object> result = new ArrayList<Object>();
+			for (Object k : mapdata.keySet()) {
+				try {
+					Object kvpair = new KeyValuePair(k, mapdata.get(k));
+					state.pushActiveContextObject(kvpair);
+					Object o = selectionCriteria.getValue(state);
+					if (o instanceof Boolean) {
+						if (((Boolean) o).booleanValue() == true) {
+							if (variant == FIRST)
+								return kvpair;
+							result.add(kvpair);
+						}
+					} else {
+						throw new SpelException(selectionCriteria.getCharPositionInLine(),
+								SpelMessages.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN);// ,selectionCriteria.stringifyAST());
+					}
+				} finally {
+					state.popActiveContextObject();
+				}
+				if ((variant == FIRST || variant == LAST) && result.size() == 0) {
+					return null;
+				}
+				if (variant == LAST) {
+					return result.get(result.size() - 1);
+				}
+			}
+			return result;
+		} else if (operand instanceof Collection) {
+			List<Object> data = new ArrayList<Object>();
+			data.addAll((Collection<?>) operand);
+			List<Object> result = new ArrayList<Object>();
+			int idx = 0;
+			for (Object element : data) {
+				try {
+					state.pushActiveContextObject(element);
+					state.enterScope("index", idx);
+					Object o = selectionCriteria.getValue(state);
+					if (o instanceof Boolean) {
+						if (((Boolean) o).booleanValue() == true) {
+							if (variant == FIRST)
+								return element;
+							result.add(element);
+						}
+					} else {
+						throw new SpelException(selectionCriteria.getCharPositionInLine(),
+								SpelMessages.RESULT_OF_SELECTION_CRITERIA_IS_NOT_BOOLEAN);// ,selectionCriteria.stringifyAST());
+					}
+					idx++;
+				} finally {
+					state.exitScope();
+					state.popActiveContextObject();
+				}
+			}
+			if ((variant == FIRST || variant == LAST) && result.size() == 0) {
+				return null;
+			}
+			if (variant == LAST) {
+				return result.get(result.size() - 1);
+			}
+			return result;
+		} else {
+			throw new SpelException(getCharPositionInLine(), SpelMessages.INVALID_TYPE_FOR_SELECTION,
+					(operand == null ? "null" : operand.getClass().getName()));
+		}
+	}
+
+	@Override
+	public String toStringAST() {
+		StringBuffer sb = new StringBuffer();
+		switch (variant) {
+		case ALL:
+			sb.append("?{");
+			break;
+		case FIRST:
+			sb.append("^{");
+			break;
+		case LAST:
+			sb.append("${");
+			break;
+		}
+		return sb.append(getChild(0).toStringAST()).append("}").toString();
+	}
+
+	@Override
+	public boolean isWritable(ExpressionState expressionState) throws SpelException {
+		return false;
+	}
+
+}

+ 126 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/SpelNode.java

@@ -0,0 +1,126 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import java.io.Serializable;
+
+import org.antlr.runtime.Token;
+import org.antlr.runtime.tree.CommonTree;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.common.ExpressionUtils;
+import org.springframework.expression.spel.ExpressionState;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.SpelMessages;
+import org.springframework.expression.spel.generated.SpringExpressionsParser;
+
+/**
+ * The common supertype of all AST nodes in a parsed Spring Expression Language format expression.
+ * 
+ * @author Andy Clement
+ * 
+ */
+public abstract class SpelNode extends CommonTree implements Serializable {
+
+	/**
+	 * The Antlr parser uses this constructor to build SpelNodes.
+	 * 
+	 * @param payload the token for the node that has been parsed
+	 */
+	protected SpelNode(Token payload) {
+		super(payload);
+	}
+
+	/**
+	 * Evaluate the expression node in the context of the supplied expression state and return the value.
+	 * 
+	 * @param expressionState the current expression state (includes the context)
+	 * @return the value of this node evaluated against the specified state
+	 */
+	public abstract Object getValue(ExpressionState expressionState) throws EvaluationException;
+
+	/**
+	 * Determine if this expression node will support a setValue() call.
+	 * 
+	 * @param expressionState the current expression state (includes the context)
+	 * @return true if the expression node will allow setValue()
+	 * @throws EvaluationException if something went wrong trying to determine if the node supports writing
+	 */
+	public boolean isWritable(ExpressionState expressionState) throws EvaluationException {
+		return false;
+	}
+
+	/**
+	 * Evaluate the expression to a node and then set the new value on that node. For example, if the expression
+	 * evaluates to a property reference then the property will be set to the new value.
+	 * 
+	 * @param expressionState the current expression state (includes the context)
+	 * @param newValue the new value
+	 * @throws EvaluationException if any problem occurs evaluating the expression or setting the new value
+	 */
+	public void setValue(ExpressionState expressionState, Object newValue) throws EvaluationException {
+		throw new SpelException(getCharPositionInLine(), SpelMessages.SETVALUE_NOT_SUPPORTED, getClass(),
+				getTokenName());
+	}
+
+	/**
+	 * @return return the token this node represents
+	 */
+	protected String getTokenName() {
+		if (getToken() == null) {
+			return "UNKNOWN";
+		}
+		return SpringExpressionsParser.tokenNames[getToken().getType()];
+	}
+
+	/**
+	 * @return the string form of this AST node
+	 */
+	public abstract String toStringAST();
+
+	/**
+	 * Helper method that returns a SpelNode rather than an Antlr Tree node.
+	 * 
+	 * @return the child node cast to a SpelNode
+	 */
+	@Override
+	public SpelNode getChild(int index) {
+		return (SpelNode) super.getChild(index);
+	}
+
+	/**
+	 * Determine the class of the object passed in, unless it is already a class object.
+	 * @param o the object that the caller wants the class of
+	 * @return the class of the object if it is not already a class object, or null if the object is null
+	 */
+	public Class<?> getObjectClass(Object o) {
+		if (o == null)
+			return null;
+		return (o instanceof Class) ? ((Class<?>) o) : o.getClass();
+	}
+
+	protected final Object getValue(ExpressionState state, Class<?> desiredReturnType) throws EvaluationException {
+		Object result = getValue(state);
+		if (result != null && desiredReturnType != null) {
+			Class<?> resultType = result.getClass();
+			if (desiredReturnType.isAssignableFrom(resultType)) {
+				return result;
+			}
+			// Attempt conversion to the requested type, may throw an exception
+			return ExpressionUtils.convert(state.getEvaluationContext(), result, desiredReturnType);
+		}
+		return result;
+	}
+}

+ 42 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/StringLiteral.java

@@ -0,0 +1,42 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+
+public class StringLiteral extends Literal {
+
+	private String value;
+
+	public StringLiteral(Token payload) {
+		super(payload);
+		value = payload.getText();
+		// TODO should these have been skipped being created by the parser rules? or not?
+		value = value.substring(1, value.length() - 1);
+		value = value.replaceAll("''", "'");
+	}
+
+	@Override
+	public String getLiteralValue() {
+		return value;
+	}
+
+	@Override
+	public String toString() {
+		return new StringBuilder("'").append(getLiteralValue()).append("'").toString();
+	}
+
+}

+ 66 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/Ternary.java

@@ -0,0 +1,66 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.spel.ExpressionState;
+import org.springframework.expression.spel.SpelException;
+
+/**
+ * Represents a ternary expression, for example: "someCheck()?true:false".
+ * 
+ * @author Andy Clement
+ */
+public class Ternary extends SpelNode {
+
+	public Ternary(Token payload) {
+		super(payload);
+	}
+
+	/**
+	 * Evaluate the condition and if true evaluate the first alternative, otherwise evaluate the second alternative.
+	 * 
+	 * @param state the expression state
+	 * @throws EvaluationException if the condition does not evaluate correctly to a boolean or there is a problem
+	 * executing the chosen alternative
+	 */
+	@Override
+	public Object getValue(ExpressionState state) throws EvaluationException {
+		Boolean b = (Boolean) getChild(0).getValue(state, Boolean.class);
+		try {
+			if (b) {
+				return getChild(1).getValue(state);
+			} else {
+				return getChild(2).getValue(state);
+			}
+		} catch (SpelException ex) {
+			ex.setPosition(getChild(0).getCharPositionInLine());
+			throw ex;
+		}
+	}
+
+	@Override
+	public String toStringAST() {
+		return new StringBuilder().append(getChild(0).toStringAST()).append(" ? ").append(getChild(1).toStringAST())
+				.append(" : ").append(getChild(2).toStringAST()).toString();
+	}
+
+	@Override
+	public boolean isWritable(ExpressionState expressionState) throws SpelException {
+		return false;
+	}
+}

+ 64 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/TypeReference.java

@@ -0,0 +1,64 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.spel.ExpressionState;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.internal.TypeCode;
+
+/**
+ * Represents a reference to a type, for example "T(String)" or "T(com.somewhere.Foo)"
+ * 
+ * @author Andy Clement
+ * 
+ */
+public class TypeReference extends SpelNode {
+
+	public TypeReference(Token payload) {
+		super(payload);
+	}
+
+	@Override
+	public Object getValue(ExpressionState state) throws EvaluationException {
+		// TODO possible optimization here if we cache the discovered type reference, but can we do that?
+		String typename = (String) getChild(0).getValue(state);
+		if (typename.indexOf(".") == -1 && Character.isLowerCase(typename.charAt(0))) {
+			TypeCode tc = TypeCode.forName(typename);
+			if (tc != TypeCode.OBJECT) {
+				// it is a primitive type
+				return tc.getType();
+			}
+		}
+		return state.findType(typename);
+	}
+
+	@Override
+	public String toStringAST() {
+		StringBuilder sb = new StringBuilder();
+		sb.append("T(");
+		sb.append(getChild(0).toStringAST());
+		sb.append(")");
+		return sb.toString();
+	}
+
+	@Override
+	public boolean isWritable(ExpressionState expressionState) throws SpelException {
+		return false;
+	}
+
+}

+ 70 - 0
spring-el/src/main/java/org/springframework/expression/spel/ast/VariableReference.java

@@ -0,0 +1,70 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.ast;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.SpelMessages;
+import org.springframework.expression.spel.ExpressionState;
+
+/**
+ * Represents a variable reference, eg. #someVar. Note this is different to a *local* variable like $someVar
+ * 
+ * @author Andy Clement
+ * 
+ */
+public class VariableReference extends SpelNode {
+
+	// Well known variables:
+	private final static String THIS = "this"; // currently active context object
+	private final static String ROOT = "root"; // root context object
+
+	private final String name;
+
+	public VariableReference(Token payload) {
+		super(payload);
+		name = payload.getText();
+	}
+
+	@Override
+	public Object getValue(ExpressionState state) throws SpelException {
+		if (name.equals(THIS))
+			return state.getActiveContextObject();
+		if (name.equals(ROOT))
+			return state.getRootContextObject();
+		Object result = state.lookupVariable(name);
+		if (result == null) {
+			throw new SpelException(getCharPositionInLine(), SpelMessages.VARIABLE_NOT_FOUND, name);
+		}
+		return result;
+	}
+
+	@Override
+	public void setValue(ExpressionState state, Object value) throws SpelException {
+		// Object oldValue = state.lookupVariable(name);
+		state.setVariable(name, value);
+	}
+
+	@Override
+	public String toStringAST() {
+		return new StringBuilder("#").append(name).toString();
+	}
+
+	@Override
+	public boolean isWritable(ExpressionState expressionState) throws SpelException {
+		return !(name.equals(THIS) || name.equals(ROOT));
+	}
+}

+ 268 - 0
spring-el/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g

@@ -0,0 +1,268 @@
+grammar SpringExpressions;
+
+options {
+	language = Java;
+	output=AST;
+	k=2;
+}
+
+tokens {
+	INTEGER_LITERAL;
+	EXPRESSION;
+	QUALIFIED_IDENTIFIER;
+	PROPERTY_OR_FIELD;
+	INDEXER;
+	CONSTRUCTOR;
+	HOLDER;
+	NAMED_ARGUMENT;
+	FUNCTIONREF;
+	TYPEREF;
+	VARIABLEREF;
+	METHOD;
+	ADD;
+	SUBTRACT;
+	NUMBER;
+}
+
+// applies only to the parser: 
+@header {package org.springframework.expression.spel.generated;}
+
+// applies only to the lexer:
+@lexer::header {package org.springframework.expression.spel.generated;}
+
+@members {
+  // For collecting info whilst processing rules that can be used in messages
+  protected Stack<String> paraphrase = new Stack<String>();
+}
+  
+@rulecatch {
+        catch(RecognitionException e) {
+                reportError(e);
+                throw e;
+        }
+}
+
+expr: expression EOF!;
+      
+expression : 
+    logicalOrExpression
+    ( (ASSIGN^ logicalOrExpression) 
+	  | (DEFAULT^ logicalOrExpression) 
+	  | (QMARK^ expression COLON! expression))?;
+
+parenExpr : LPAREN! expression RPAREN!;
+	
+logicalOrExpression 
+: logicalAndExpression (OR^ logicalAndExpression)*;
+                        
+logicalAndExpression 
+: relationalExpression (AND^ relationalExpression)*;
+	
+relationalExpression : sumExpression (relationalOperator^ sumExpression)?;
+
+sumExpression
+	: productExpression ( (PLUS^ | MINUS^) productExpression)*;
+
+productExpression
+	: powerExpr ((STAR^ | DIV^| MOD^) powerExpr)* ;
+
+powerExpr  : unaryExpression (POWER^ unaryExpression)? ;
+
+unaryExpression 
+	:	(PLUS^ | MINUS^ | BANG^) unaryExpression	
+	|	primaryExpression ;
+	
+primaryExpression
+    : startNode (node)? -> ^(EXPRESSION startNode (node)?);
+
+startNode 
+    : 
+    parenExpr
+    | methodOrProperty 
+    | functionOrVar
+    | indexer
+    | literal
+    | type
+    | constructor
+    | projection 
+    | selection 
+    | firstSelection
+    | lastSelection
+    ;
+    
+node
+	: ((DOT dottedNode) | nonDottedNode)+;
+	
+nonDottedNode
+	:	indexer;
+
+dottedNode
+	:	
+	((methodOrProperty 
+	| functionOrVar
+    | projection 
+    | selection 
+    | firstSelection 
+    | lastSelection 
+    ))
+	;
+	
+functionOrVar 
+    : (POUND ID LPAREN) => function
+    | var
+    ;
+    
+function : POUND id=ID methodArgs -> ^(FUNCTIONREF[$id] methodArgs);
+    
+var : POUND id=ID -> ^(VARIABLEREF[$id]); 
+
+
+methodOrProperty
+	:	(ID LPAREN) => id=ID methodArgs -> ^(METHOD[$id] methodArgs)
+	|	property
+	;
+	
+// may have to preserve these commas to make it easier to offer suggestions in the right place
+// mod at 9th feb 19:13 - added the second 'COMMA?' to allow for code completion "foo(A,"
+// TODO need to preserve commas and then check for badly formed call later (optimizing tree walk) to disallow "foo(a,b,c,)"
+methodArgs :  LPAREN! (argument (COMMA! argument)* (COMMA!)?)? RPAREN!;
+
+// If we match ID then create a node called PROPERTY_OR_FIELD and copy the id info into it.
+// this means the propertyOrField.text is what id.text would have been, rather than having to
+// access id as a child of the new node.
+property: id=ID -> ^(PROPERTY_OR_FIELD[$id]);
+
+
+indexer: LBRACKET r1=argument (COMMA r2=argument)* RBRACKET -> ^(INDEXER $r1 ($r2)*);
+	
+// argument;
+	// TODO make expression conditional with ? if want completion for when the RCURLY is missing
+projection: PROJECT^ expression RCURLY!;
+
+selection: SELECT^ expression RCURLY!;
+
+firstSelection:	SELECT_FIRST^ expression RCURLY!;
+
+lastSelection: SELECT_LAST^ expression RCURLY!;
+
+// TODO cope with array types
+type:	TYPE qualifiedId RPAREN -> ^(TYPEREF qualifiedId);
+//type:   TYPE tn=qualifiedId (LBRACKET RBRACKET)? (COMMA qid=qualifiedId)? RPAREN
+
+
+constructor  
+	:	('new' qualifiedId LPAREN) => 'new' qualifiedId ctorArgs -> ^(CONSTRUCTOR qualifiedId ctorArgs)
+	;
+	
+ctorArgs
+	: LPAREN! (namedArgument (COMMA! namedArgument)*)? RPAREN!;
+
+argument : expression;
+
+namedArgument 
+    : (ID ASSIGN) => id=ID ASSIGN expression 
+                  -> ^(NAMED_ARGUMENT[$id] expression)
+    | argument ;
+  	
+qualifiedId : ID (DOT ID)* -> ^(QUALIFIED_IDENTIFIER ID*);
+
+contextName : ID (DIV ID)* -> ^(QUALIFIED_IDENTIFIER ID*);
+	
+literal
+	: INTEGER_LITERAL 
+	| STRING_LITERAL
+	| DQ_STRING_LITERAL
+	| boolLiteral
+	| NULL_LITERAL
+	| HEXADECIMAL_INTEGER_LITERAL 
+	| REAL_LITERAL
+	;
+	
+boolLiteral: TRUE | FALSE;
+
+INTEGER_LITERAL
+	: (DECIMAL_DIGIT)+ (INTEGER_TYPE_SUFFIX)?;
+
+HEXADECIMAL_INTEGER_LITERAL : ('0x' | '0X') (HEX_DIGIT)+ (INTEGER_TYPE_SUFFIX)?;
+
+relationalOperator
+    :   EQUAL 
+    |   NOT_EQUAL
+    |   LESS_THAN
+    |   LESS_THAN_OR_EQUAL      
+    |   GREATER_THAN            
+    |   GREATER_THAN_OR_EQUAL 
+    |   INSTANCEOF   
+    |   BETWEEN   
+    |   MATCHES
+    ; 
+          
+ASSIGN: '=';
+EQUAL: '==';
+NOT_EQUAL: '!=';
+LESS_THAN: '<';
+LESS_THAN_OR_EQUAL: '<=';
+GREATER_THAN: '>';
+GREATER_THAN_OR_EQUAL: '>=';
+INSTANCEOF:     'instanceof';
+BETWEEN:'between';
+MATCHES:'matches';
+NULL_LITERAL: 'null';
+
+SEMI: ';';
+DOT:    '.';
+COMMA:	',';
+LPAREN: '(';
+RPAREN: ')';
+LCURLY: '{';
+RCURLY: '}';
+LBRACKET: '[';
+RBRACKET: ']';
+PIPE:	'|';
+
+AND:    'and';
+OR:     'or';
+FALSE:  'false';
+TRUE:   'true';
+
+PLUS: '+';
+MINUS: '-';
+DIV: '/';
+STAR: '*';
+MOD: '%';
+POWER: '^';
+BANG: '!';
+POUND: '#';
+QMARK: '?';
+DEFAULT: '??';
+PROJECT: '!{';
+SELECT: '?{';
+SELECT_FIRST: '^{';
+SELECT_LAST: '${';
+TYPE: 'T(';
+
+STRING_LITERAL:	'\''! (APOS|~'\'')* '\''!;
+DQ_STRING_LITERAL:	'"'! (~'"')* '"'!;
+ID:	('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9'|DOT_ESCAPED)*;
+DOT_ESCAPED: '\\.';
+WS: ( ' ' | '\t' | '\n' |'\r')+ { $channel=HIDDEN; } ;
+DOLLAR:	'$';
+AT: '@';
+UPTO: '..';
+COLON: ':';
+
+
+REAL_LITERAL :	
+  ('.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) |
+	((DECIMAL_DIGIT)+ '.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) |
+	((DECIMAL_DIGIT)+ (EXPONENT_PART) (REAL_TYPE_SUFFIX)?) |
+	((DECIMAL_DIGIT)+ (REAL_TYPE_SUFFIX));
+
+fragment APOS : '\''! '\'';
+fragment DECIMAL_DIGIT : '0'..'9' ;
+fragment INTEGER_TYPE_SUFFIX : ( 'L' | 'l' );
+fragment HEX_DIGIT : '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'|'A'|'B'|'C'|'D'|'E'|'F'|'a'|'b'|'c'|'d'|'e'|'f';		
+	
+fragment EXPONENT_PART : 'e'  (SIGN)*  (DECIMAL_DIGIT)+ | 'E'  (SIGN)*  (DECIMAL_DIGIT)+ ;	
+fragment SIGN :	'+' | '-' ;
+fragment REAL_TYPE_SUFFIX : 'F' | 'f' | 'D' | 'd';

+ 74 - 0
spring-el/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.tokens

@@ -0,0 +1,74 @@
+GREATER_THAN_OR_EQUAL=61
+HOLDER=10
+COMMA=37
+SELECT_FIRST=43
+GREATER_THAN=60
+TYPE=45
+MINUS=28
+SELECT_LAST=44
+NUMBER=18
+LESS_THAN=58
+BANG=33
+FALSE=52
+METHOD=15
+PROPERTY_OR_FIELD=7
+LBRACKET=38
+INDEXER=8
+MOD=31
+FUNCTIONREF=12
+NULL_LITERAL=48
+NAMED_ARGUMENT=11
+OR=25
+PIPE=67
+DOT=34
+RCURLY=41
+EXPRESSION=5
+AND=26
+LCURLY=66
+REAL_TYPE_SUFFIX=75
+STRING_LITERAL=46
+QUALIFIED_IDENTIFIER=6
+SELECT=42
+ASSIGN=19
+SUBTRACT=17
+RBRACKET=39
+INSTANCEOF=62
+BETWEEN=63
+RPAREN=24
+SIGN=76
+LPAREN=23
+HEX_DIGIT=55
+PLUS=27
+APOS=68
+INTEGER_LITERAL=4
+AT=72
+ID=36
+NOT_EQUAL=57
+POWER=32
+TYPEREF=13
+DECIMAL_DIGIT=53
+WS=70
+DOLLAR=71
+LESS_THAN_OR_EQUAL=59
+DQ_STRING_LITERAL=47
+HEXADECIMAL_INTEGER_LITERAL=49
+SEMI=65
+CONSTRUCTOR=9
+INTEGER_TYPE_SUFFIX=54
+EQUAL=56
+MATCHES=64
+DOT_ESCAPED=69
+UPTO=73
+QMARK=21
+DEFAULT=20
+COLON=22
+PROJECT=40
+DIV=30
+STAR=29
+REAL_LITERAL=50
+VARIABLEREF=14
+EXPONENT_PART=74
+TRUE=51
+ADD=16
+POUND=35
+'new'=77

+ 2687 - 0
spring-el/src/main/java/org/springframework/expression/spel/generated/SpringExpressionsLexer.java

@@ -0,0 +1,2687 @@
+// $ANTLR 3.0.1 /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g 2008-09-16 19:06:07
+package org.springframework.expression.spel.generated;
+
+import org.antlr.runtime.*;
+import java.util.Stack;
+import java.util.List;
+import java.util.ArrayList;
+
+public class SpringExpressionsLexer extends Lexer {
+    public static final int GREATER_THAN_OR_EQUAL=61;
+    public static final int HOLDER=10;
+    public static final int COMMA=37;
+    public static final int SELECT_FIRST=43;
+    public static final int TYPE=45;
+    public static final int GREATER_THAN=60;
+    public static final int MINUS=28;
+    public static final int SELECT_LAST=44;
+    public static final int NUMBER=18;
+    public static final int BANG=33;
+    public static final int LESS_THAN=58;
+    public static final int METHOD=15;
+    public static final int FALSE=52;
+    public static final int PROPERTY_OR_FIELD=7;
+    public static final int LBRACKET=38;
+    public static final int INDEXER=8;
+    public static final int MOD=31;
+    public static final int FUNCTIONREF=12;
+    public static final int NULL_LITERAL=48;
+    public static final int NAMED_ARGUMENT=11;
+    public static final int OR=25;
+    public static final int PIPE=67;
+    public static final int DOT=34;
+    public static final int RCURLY=41;
+    public static final int EXPRESSION=5;
+    public static final int AND=26;
+    public static final int LCURLY=66;
+    public static final int REAL_TYPE_SUFFIX=75;
+    public static final int STRING_LITERAL=46;
+    public static final int QUALIFIED_IDENTIFIER=6;
+    public static final int SELECT=42;
+    public static final int ASSIGN=19;
+    public static final int SUBTRACT=17;
+    public static final int RBRACKET=39;
+    public static final int INSTANCEOF=62;
+    public static final int BETWEEN=63;
+    public static final int RPAREN=24;
+    public static final int SIGN=76;
+    public static final int LPAREN=23;
+    public static final int T77=77;
+    public static final int HEX_DIGIT=55;
+    public static final int PLUS=27;
+    public static final int APOS=68;
+    public static final int INTEGER_LITERAL=4;
+    public static final int AT=72;
+    public static final int ID=36;
+    public static final int NOT_EQUAL=57;
+    public static final int POWER=32;
+    public static final int TYPEREF=13;
+    public static final int DECIMAL_DIGIT=53;
+    public static final int WS=70;
+    public static final int DOLLAR=71;
+    public static final int LESS_THAN_OR_EQUAL=59;
+    public static final int DQ_STRING_LITERAL=47;
+    public static final int HEXADECIMAL_INTEGER_LITERAL=49;
+    public static final int CONSTRUCTOR=9;
+    public static final int SEMI=65;
+    public static final int INTEGER_TYPE_SUFFIX=54;
+    public static final int EQUAL=56;
+    public static final int MATCHES=64;
+    public static final int DOT_ESCAPED=69;
+    public static final int QMARK=21;
+    public static final int UPTO=73;
+    public static final int EOF=-1;
+    public static final int Tokens=78;
+    public static final int PROJECT=40;
+    public static final int COLON=22;
+    public static final int DEFAULT=20;
+    public static final int DIV=30;
+    public static final int STAR=29;
+    public static final int VARIABLEREF=14;
+    public static final int REAL_LITERAL=50;
+    public static final int ADD=16;
+    public static final int TRUE=51;
+    public static final int EXPONENT_PART=74;
+    public static final int POUND=35;
+    public SpringExpressionsLexer() {;} 
+    public SpringExpressionsLexer(CharStream input) {
+        super(input);
+    }
+    public String getGrammarFileName() { return "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g"; }
+
+    // $ANTLR start T77
+    public final void mT77() throws RecognitionException {
+        try {
+            int _type = T77;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:8:5: ( 'new' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:8:7: 'new'
+            {
+            match("new"); 
+
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end T77
+
+    // $ANTLR start INTEGER_LITERAL
+    public final void mINTEGER_LITERAL() throws RecognitionException {
+        try {
+            int _type = INTEGER_LITERAL;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:184:2: ( ( DECIMAL_DIGIT )+ ( INTEGER_TYPE_SUFFIX )? )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:184:4: ( DECIMAL_DIGIT )+ ( INTEGER_TYPE_SUFFIX )?
+            {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:184:4: ( DECIMAL_DIGIT )+
+            int cnt1=0;
+            loop1:
+            do {
+                int alt1=2;
+                int LA1_0 = input.LA(1);
+
+                if ( ((LA1_0>='0' && LA1_0<='9')) ) {
+                    alt1=1;
+                }
+
+
+                switch (alt1) {
+            	case 1 :
+            	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:184:5: DECIMAL_DIGIT
+            	    {
+            	    mDECIMAL_DIGIT(); 
+
+            	    }
+            	    break;
+
+            	default :
+            	    if ( cnt1 >= 1 ) break loop1;
+                        EarlyExitException eee =
+                            new EarlyExitException(1, input);
+                        throw eee;
+                }
+                cnt1++;
+            } while (true);
+
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:184:21: ( INTEGER_TYPE_SUFFIX )?
+            int alt2=2;
+            int LA2_0 = input.LA(1);
+
+            if ( (LA2_0=='L'||LA2_0=='l') ) {
+                alt2=1;
+            }
+            switch (alt2) {
+                case 1 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:184:22: INTEGER_TYPE_SUFFIX
+                    {
+                    mINTEGER_TYPE_SUFFIX(); 
+
+                    }
+                    break;
+
+            }
+
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end INTEGER_LITERAL
+
+    // $ANTLR start HEXADECIMAL_INTEGER_LITERAL
+    public final void mHEXADECIMAL_INTEGER_LITERAL() throws RecognitionException {
+        try {
+            int _type = HEXADECIMAL_INTEGER_LITERAL;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:186:29: ( ( '0x' | '0X' ) ( HEX_DIGIT )+ ( INTEGER_TYPE_SUFFIX )? )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:186:31: ( '0x' | '0X' ) ( HEX_DIGIT )+ ( INTEGER_TYPE_SUFFIX )?
+            {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:186:31: ( '0x' | '0X' )
+            int alt3=2;
+            int LA3_0 = input.LA(1);
+
+            if ( (LA3_0=='0') ) {
+                int LA3_1 = input.LA(2);
+
+                if ( (LA3_1=='x') ) {
+                    alt3=1;
+                }
+                else if ( (LA3_1=='X') ) {
+                    alt3=2;
+                }
+                else {
+                    NoViableAltException nvae =
+                        new NoViableAltException("186:31: ( '0x' | '0X' )", 3, 1, input);
+
+                    throw nvae;
+                }
+            }
+            else {
+                NoViableAltException nvae =
+                    new NoViableAltException("186:31: ( '0x' | '0X' )", 3, 0, input);
+
+                throw nvae;
+            }
+            switch (alt3) {
+                case 1 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:186:32: '0x'
+                    {
+                    match("0x"); 
+
+
+                    }
+                    break;
+                case 2 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:186:39: '0X'
+                    {
+                    match("0X"); 
+
+
+                    }
+                    break;
+
+            }
+
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:186:45: ( HEX_DIGIT )+
+            int cnt4=0;
+            loop4:
+            do {
+                int alt4=2;
+                int LA4_0 = input.LA(1);
+
+                if ( ((LA4_0>='0' && LA4_0<='9')||(LA4_0>='A' && LA4_0<='F')||(LA4_0>='a' && LA4_0<='f')) ) {
+                    alt4=1;
+                }
+
+
+                switch (alt4) {
+            	case 1 :
+            	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:186:46: HEX_DIGIT
+            	    {
+            	    mHEX_DIGIT(); 
+
+            	    }
+            	    break;
+
+            	default :
+            	    if ( cnt4 >= 1 ) break loop4;
+                        EarlyExitException eee =
+                            new EarlyExitException(4, input);
+                        throw eee;
+                }
+                cnt4++;
+            } while (true);
+
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:186:58: ( INTEGER_TYPE_SUFFIX )?
+            int alt5=2;
+            int LA5_0 = input.LA(1);
+
+            if ( (LA5_0=='L'||LA5_0=='l') ) {
+                alt5=1;
+            }
+            switch (alt5) {
+                case 1 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:186:59: INTEGER_TYPE_SUFFIX
+                    {
+                    mINTEGER_TYPE_SUFFIX(); 
+
+                    }
+                    break;
+
+            }
+
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end HEXADECIMAL_INTEGER_LITERAL
+
+    // $ANTLR start ASSIGN
+    public final void mASSIGN() throws RecognitionException {
+        try {
+            int _type = ASSIGN;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:200:7: ( '=' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:200:9: '='
+            {
+            match('='); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end ASSIGN
+
+    // $ANTLR start EQUAL
+    public final void mEQUAL() throws RecognitionException {
+        try {
+            int _type = EQUAL;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:201:6: ( '==' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:201:8: '=='
+            {
+            match("=="); 
+
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end EQUAL
+
+    // $ANTLR start NOT_EQUAL
+    public final void mNOT_EQUAL() throws RecognitionException {
+        try {
+            int _type = NOT_EQUAL;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:202:10: ( '!=' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:202:12: '!='
+            {
+            match("!="); 
+
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end NOT_EQUAL
+
+    // $ANTLR start LESS_THAN
+    public final void mLESS_THAN() throws RecognitionException {
+        try {
+            int _type = LESS_THAN;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:203:10: ( '<' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:203:12: '<'
+            {
+            match('<'); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end LESS_THAN
+
+    // $ANTLR start LESS_THAN_OR_EQUAL
+    public final void mLESS_THAN_OR_EQUAL() throws RecognitionException {
+        try {
+            int _type = LESS_THAN_OR_EQUAL;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:204:19: ( '<=' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:204:21: '<='
+            {
+            match("<="); 
+
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end LESS_THAN_OR_EQUAL
+
+    // $ANTLR start GREATER_THAN
+    public final void mGREATER_THAN() throws RecognitionException {
+        try {
+            int _type = GREATER_THAN;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:205:13: ( '>' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:205:15: '>'
+            {
+            match('>'); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end GREATER_THAN
+
+    // $ANTLR start GREATER_THAN_OR_EQUAL
+    public final void mGREATER_THAN_OR_EQUAL() throws RecognitionException {
+        try {
+            int _type = GREATER_THAN_OR_EQUAL;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:206:22: ( '>=' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:206:24: '>='
+            {
+            match(">="); 
+
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end GREATER_THAN_OR_EQUAL
+
+    // $ANTLR start INSTANCEOF
+    public final void mINSTANCEOF() throws RecognitionException {
+        try {
+            int _type = INSTANCEOF;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:207:11: ( 'instanceof' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:207:17: 'instanceof'
+            {
+            match("instanceof"); 
+
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end INSTANCEOF
+
+    // $ANTLR start BETWEEN
+    public final void mBETWEEN() throws RecognitionException {
+        try {
+            int _type = BETWEEN;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:208:8: ( 'between' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:208:9: 'between'
+            {
+            match("between"); 
+
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end BETWEEN
+
+    // $ANTLR start MATCHES
+    public final void mMATCHES() throws RecognitionException {
+        try {
+            int _type = MATCHES;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:209:8: ( 'matches' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:209:9: 'matches'
+            {
+            match("matches"); 
+
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end MATCHES
+
+    // $ANTLR start NULL_LITERAL
+    public final void mNULL_LITERAL() throws RecognitionException {
+        try {
+            int _type = NULL_LITERAL;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:210:13: ( 'null' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:210:15: 'null'
+            {
+            match("null"); 
+
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end NULL_LITERAL
+
+    // $ANTLR start SEMI
+    public final void mSEMI() throws RecognitionException {
+        try {
+            int _type = SEMI;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:212:5: ( ';' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:212:7: ';'
+            {
+            match(';'); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end SEMI
+
+    // $ANTLR start DOT
+    public final void mDOT() throws RecognitionException {
+        try {
+            int _type = DOT;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:213:4: ( '.' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:213:9: '.'
+            {
+            match('.'); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end DOT
+
+    // $ANTLR start COMMA
+    public final void mCOMMA() throws RecognitionException {
+        try {
+            int _type = COMMA;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:214:6: ( ',' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:214:8: ','
+            {
+            match(','); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end COMMA
+
+    // $ANTLR start LPAREN
+    public final void mLPAREN() throws RecognitionException {
+        try {
+            int _type = LPAREN;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:215:7: ( '(' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:215:9: '('
+            {
+            match('('); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end LPAREN
+
+    // $ANTLR start RPAREN
+    public final void mRPAREN() throws RecognitionException {
+        try {
+            int _type = RPAREN;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:216:7: ( ')' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:216:9: ')'
+            {
+            match(')'); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end RPAREN
+
+    // $ANTLR start LCURLY
+    public final void mLCURLY() throws RecognitionException {
+        try {
+            int _type = LCURLY;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:217:7: ( '{' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:217:9: '{'
+            {
+            match('{'); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end LCURLY
+
+    // $ANTLR start RCURLY
+    public final void mRCURLY() throws RecognitionException {
+        try {
+            int _type = RCURLY;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:218:7: ( '}' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:218:9: '}'
+            {
+            match('}'); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end RCURLY
+
+    // $ANTLR start LBRACKET
+    public final void mLBRACKET() throws RecognitionException {
+        try {
+            int _type = LBRACKET;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:219:9: ( '[' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:219:11: '['
+            {
+            match('['); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end LBRACKET
+
+    // $ANTLR start RBRACKET
+    public final void mRBRACKET() throws RecognitionException {
+        try {
+            int _type = RBRACKET;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:220:9: ( ']' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:220:11: ']'
+            {
+            match(']'); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end RBRACKET
+
+    // $ANTLR start PIPE
+    public final void mPIPE() throws RecognitionException {
+        try {
+            int _type = PIPE;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:221:5: ( '|' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:221:7: '|'
+            {
+            match('|'); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end PIPE
+
+    // $ANTLR start AND
+    public final void mAND() throws RecognitionException {
+        try {
+            int _type = AND;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:223:4: ( 'and' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:223:9: 'and'
+            {
+            match("and"); 
+
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end AND
+
+    // $ANTLR start OR
+    public final void mOR() throws RecognitionException {
+        try {
+            int _type = OR;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:224:3: ( 'or' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:224:9: 'or'
+            {
+            match("or"); 
+
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end OR
+
+    // $ANTLR start FALSE
+    public final void mFALSE() throws RecognitionException {
+        try {
+            int _type = FALSE;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:225:6: ( 'false' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:225:9: 'false'
+            {
+            match("false"); 
+
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end FALSE
+
+    // $ANTLR start TRUE
+    public final void mTRUE() throws RecognitionException {
+        try {
+            int _type = TRUE;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:226:5: ( 'true' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:226:9: 'true'
+            {
+            match("true"); 
+
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end TRUE
+
+    // $ANTLR start PLUS
+    public final void mPLUS() throws RecognitionException {
+        try {
+            int _type = PLUS;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:228:5: ( '+' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:228:7: '+'
+            {
+            match('+'); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end PLUS
+
+    // $ANTLR start MINUS
+    public final void mMINUS() throws RecognitionException {
+        try {
+            int _type = MINUS;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:229:6: ( '-' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:229:8: '-'
+            {
+            match('-'); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end MINUS
+
+    // $ANTLR start DIV
+    public final void mDIV() throws RecognitionException {
+        try {
+            int _type = DIV;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:230:4: ( '/' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:230:6: '/'
+            {
+            match('/'); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end DIV
+
+    // $ANTLR start STAR
+    public final void mSTAR() throws RecognitionException {
+        try {
+            int _type = STAR;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:231:5: ( '*' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:231:7: '*'
+            {
+            match('*'); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end STAR
+
+    // $ANTLR start MOD
+    public final void mMOD() throws RecognitionException {
+        try {
+            int _type = MOD;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:232:4: ( '%' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:232:6: '%'
+            {
+            match('%'); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end MOD
+
+    // $ANTLR start POWER
+    public final void mPOWER() throws RecognitionException {
+        try {
+            int _type = POWER;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:233:6: ( '^' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:233:8: '^'
+            {
+            match('^'); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end POWER
+
+    // $ANTLR start BANG
+    public final void mBANG() throws RecognitionException {
+        try {
+            int _type = BANG;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:234:5: ( '!' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:234:7: '!'
+            {
+            match('!'); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end BANG
+
+    // $ANTLR start POUND
+    public final void mPOUND() throws RecognitionException {
+        try {
+            int _type = POUND;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:235:6: ( '#' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:235:8: '#'
+            {
+            match('#'); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end POUND
+
+    // $ANTLR start QMARK
+    public final void mQMARK() throws RecognitionException {
+        try {
+            int _type = QMARK;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:236:6: ( '?' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:236:8: '?'
+            {
+            match('?'); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end QMARK
+
+    // $ANTLR start DEFAULT
+    public final void mDEFAULT() throws RecognitionException {
+        try {
+            int _type = DEFAULT;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:237:8: ( '??' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:237:10: '??'
+            {
+            match("??"); 
+
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end DEFAULT
+
+    // $ANTLR start PROJECT
+    public final void mPROJECT() throws RecognitionException {
+        try {
+            int _type = PROJECT;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:238:8: ( '!{' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:238:10: '!{'
+            {
+            match("!{"); 
+
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end PROJECT
+
+    // $ANTLR start SELECT
+    public final void mSELECT() throws RecognitionException {
+        try {
+            int _type = SELECT;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:239:7: ( '?{' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:239:9: '?{'
+            {
+            match("?{"); 
+
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end SELECT
+
+    // $ANTLR start SELECT_FIRST
+    public final void mSELECT_FIRST() throws RecognitionException {
+        try {
+            int _type = SELECT_FIRST;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:240:13: ( '^{' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:240:15: '^{'
+            {
+            match("^{"); 
+
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end SELECT_FIRST
+
+    // $ANTLR start SELECT_LAST
+    public final void mSELECT_LAST() throws RecognitionException {
+        try {
+            int _type = SELECT_LAST;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:241:12: ( '${' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:241:14: '${'
+            {
+            match("${"); 
+
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end SELECT_LAST
+
+    // $ANTLR start TYPE
+    public final void mTYPE() throws RecognitionException {
+        try {
+            int _type = TYPE;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:242:5: ( 'T(' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:242:7: 'T('
+            {
+            match("T("); 
+
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end TYPE
+
+    // $ANTLR start STRING_LITERAL
+    public final void mSTRING_LITERAL() throws RecognitionException {
+        try {
+            int _type = STRING_LITERAL;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:244:15: ( '\\'' ( APOS | ~ '\\'' )* '\\'' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:244:17: '\\'' ( APOS | ~ '\\'' )* '\\''
+            {
+            match('\''); 
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:244:23: ( APOS | ~ '\\'' )*
+            loop6:
+            do {
+                int alt6=3;
+                int LA6_0 = input.LA(1);
+
+                if ( (LA6_0=='\'') ) {
+                    int LA6_1 = input.LA(2);
+
+                    if ( (LA6_1=='\'') ) {
+                        alt6=1;
+                    }
+
+
+                }
+                else if ( ((LA6_0>='\u0000' && LA6_0<='&')||(LA6_0>='(' && LA6_0<='\uFFFE')) ) {
+                    alt6=2;
+                }
+
+
+                switch (alt6) {
+            	case 1 :
+            	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:244:24: APOS
+            	    {
+            	    mAPOS(); 
+
+            	    }
+            	    break;
+            	case 2 :
+            	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:244:29: ~ '\\''
+            	    {
+            	    if ( (input.LA(1)>='\u0000' && input.LA(1)<='&')||(input.LA(1)>='(' && input.LA(1)<='\uFFFE') ) {
+            	        input.consume();
+
+            	    }
+            	    else {
+            	        MismatchedSetException mse =
+            	            new MismatchedSetException(null,input);
+            	        recover(mse);    throw mse;
+            	    }
+
+
+            	    }
+            	    break;
+
+            	default :
+            	    break loop6;
+                }
+            } while (true);
+
+            match('\''); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end STRING_LITERAL
+
+    // $ANTLR start DQ_STRING_LITERAL
+    public final void mDQ_STRING_LITERAL() throws RecognitionException {
+        try {
+            int _type = DQ_STRING_LITERAL;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:245:18: ( '\"' (~ '\"' )* '\"' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:245:20: '\"' (~ '\"' )* '\"'
+            {
+            match('\"'); 
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:245:25: (~ '\"' )*
+            loop7:
+            do {
+                int alt7=2;
+                int LA7_0 = input.LA(1);
+
+                if ( ((LA7_0>='\u0000' && LA7_0<='!')||(LA7_0>='#' && LA7_0<='\uFFFE')) ) {
+                    alt7=1;
+                }
+
+
+                switch (alt7) {
+            	case 1 :
+            	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:245:26: ~ '\"'
+            	    {
+            	    if ( (input.LA(1)>='\u0000' && input.LA(1)<='!')||(input.LA(1)>='#' && input.LA(1)<='\uFFFE') ) {
+            	        input.consume();
+
+            	    }
+            	    else {
+            	        MismatchedSetException mse =
+            	            new MismatchedSetException(null,input);
+            	        recover(mse);    throw mse;
+            	    }
+
+
+            	    }
+            	    break;
+
+            	default :
+            	    break loop7;
+                }
+            } while (true);
+
+            match('\"'); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end DQ_STRING_LITERAL
+
+    // $ANTLR start ID
+    public final void mID() throws RecognitionException {
+        try {
+            int _type = ID;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:246:3: ( ( 'a' .. 'z' | 'A' .. 'Z' | '_' ) ( 'a' .. 'z' | 'A' .. 'Z' | '_' | '0' .. '9' | DOT_ESCAPED )* )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:246:5: ( 'a' .. 'z' | 'A' .. 'Z' | '_' ) ( 'a' .. 'z' | 'A' .. 'Z' | '_' | '0' .. '9' | DOT_ESCAPED )*
+            {
+            if ( (input.LA(1)>='A' && input.LA(1)<='Z')||input.LA(1)=='_'||(input.LA(1)>='a' && input.LA(1)<='z') ) {
+                input.consume();
+
+            }
+            else {
+                MismatchedSetException mse =
+                    new MismatchedSetException(null,input);
+                recover(mse);    throw mse;
+            }
+
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:246:29: ( 'a' .. 'z' | 'A' .. 'Z' | '_' | '0' .. '9' | DOT_ESCAPED )*
+            loop8:
+            do {
+                int alt8=6;
+                switch ( input.LA(1) ) {
+                case 'a':
+                case 'b':
+                case 'c':
+                case 'd':
+                case 'e':
+                case 'f':
+                case 'g':
+                case 'h':
+                case 'i':
+                case 'j':
+                case 'k':
+                case 'l':
+                case 'm':
+                case 'n':
+                case 'o':
+                case 'p':
+                case 'q':
+                case 'r':
+                case 's':
+                case 't':
+                case 'u':
+                case 'v':
+                case 'w':
+                case 'x':
+                case 'y':
+                case 'z':
+                    {
+                    alt8=1;
+                    }
+                    break;
+                case 'A':
+                case 'B':
+                case 'C':
+                case 'D':
+                case 'E':
+                case 'F':
+                case 'G':
+                case 'H':
+                case 'I':
+                case 'J':
+                case 'K':
+                case 'L':
+                case 'M':
+                case 'N':
+                case 'O':
+                case 'P':
+                case 'Q':
+                case 'R':
+                case 'S':
+                case 'T':
+                case 'U':
+                case 'V':
+                case 'W':
+                case 'X':
+                case 'Y':
+                case 'Z':
+                    {
+                    alt8=2;
+                    }
+                    break;
+                case '_':
+                    {
+                    alt8=3;
+                    }
+                    break;
+                case '0':
+                case '1':
+                case '2':
+                case '3':
+                case '4':
+                case '5':
+                case '6':
+                case '7':
+                case '8':
+                case '9':
+                    {
+                    alt8=4;
+                    }
+                    break;
+                case '\\':
+                    {
+                    alt8=5;
+                    }
+                    break;
+
+                }
+
+                switch (alt8) {
+            	case 1 :
+            	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:246:30: 'a' .. 'z'
+            	    {
+            	    matchRange('a','z'); 
+
+            	    }
+            	    break;
+            	case 2 :
+            	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:246:39: 'A' .. 'Z'
+            	    {
+            	    matchRange('A','Z'); 
+
+            	    }
+            	    break;
+            	case 3 :
+            	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:246:48: '_'
+            	    {
+            	    match('_'); 
+
+            	    }
+            	    break;
+            	case 4 :
+            	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:246:52: '0' .. '9'
+            	    {
+            	    matchRange('0','9'); 
+
+            	    }
+            	    break;
+            	case 5 :
+            	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:246:61: DOT_ESCAPED
+            	    {
+            	    mDOT_ESCAPED(); 
+
+            	    }
+            	    break;
+
+            	default :
+            	    break loop8;
+                }
+            } while (true);
+
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end ID
+
+    // $ANTLR start DOT_ESCAPED
+    public final void mDOT_ESCAPED() throws RecognitionException {
+        try {
+            int _type = DOT_ESCAPED;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:247:12: ( '\\\\.' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:247:14: '\\\\.'
+            {
+            match("\\."); 
+
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end DOT_ESCAPED
+
+    // $ANTLR start WS
+    public final void mWS() throws RecognitionException {
+        try {
+            int _type = WS;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:248:3: ( ( ' ' | '\\t' | '\\n' | '\\r' )+ )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:248:5: ( ' ' | '\\t' | '\\n' | '\\r' )+
+            {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:248:5: ( ' ' | '\\t' | '\\n' | '\\r' )+
+            int cnt9=0;
+            loop9:
+            do {
+                int alt9=2;
+                int LA9_0 = input.LA(1);
+
+                if ( ((LA9_0>='\t' && LA9_0<='\n')||LA9_0=='\r'||LA9_0==' ') ) {
+                    alt9=1;
+                }
+
+
+                switch (alt9) {
+            	case 1 :
+            	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:
+            	    {
+            	    if ( (input.LA(1)>='\t' && input.LA(1)<='\n')||input.LA(1)=='\r'||input.LA(1)==' ' ) {
+            	        input.consume();
+
+            	    }
+            	    else {
+            	        MismatchedSetException mse =
+            	            new MismatchedSetException(null,input);
+            	        recover(mse);    throw mse;
+            	    }
+
+
+            	    }
+            	    break;
+
+            	default :
+            	    if ( cnt9 >= 1 ) break loop9;
+                        EarlyExitException eee =
+                            new EarlyExitException(9, input);
+                        throw eee;
+                }
+                cnt9++;
+            } while (true);
+
+             channel=HIDDEN; 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end WS
+
+    // $ANTLR start DOLLAR
+    public final void mDOLLAR() throws RecognitionException {
+        try {
+            int _type = DOLLAR;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:249:7: ( '$' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:249:9: '$'
+            {
+            match('$'); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end DOLLAR
+
+    // $ANTLR start AT
+    public final void mAT() throws RecognitionException {
+        try {
+            int _type = AT;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:250:3: ( '@' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:250:5: '@'
+            {
+            match('@'); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end AT
+
+    // $ANTLR start UPTO
+    public final void mUPTO() throws RecognitionException {
+        try {
+            int _type = UPTO;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:251:5: ( '..' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:251:7: '..'
+            {
+            match(".."); 
+
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end UPTO
+
+    // $ANTLR start COLON
+    public final void mCOLON() throws RecognitionException {
+        try {
+            int _type = COLON;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:252:6: ( ':' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:252:8: ':'
+            {
+            match(':'); 
+
+            }
+
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end COLON
+
+    // $ANTLR start REAL_LITERAL
+    public final void mREAL_LITERAL() throws RecognitionException {
+        try {
+            int _type = REAL_LITERAL;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:255:14: ( ( '.' ( DECIMAL_DIGIT )+ ( EXPONENT_PART )? ( REAL_TYPE_SUFFIX )? ) | ( ( DECIMAL_DIGIT )+ '.' ( DECIMAL_DIGIT )+ ( EXPONENT_PART )? ( REAL_TYPE_SUFFIX )? ) | ( ( DECIMAL_DIGIT )+ ( EXPONENT_PART ) ( REAL_TYPE_SUFFIX )? ) | ( ( DECIMAL_DIGIT )+ ( REAL_TYPE_SUFFIX ) ) )
+            int alt20=4;
+            alt20 = dfa20.predict(input);
+            switch (alt20) {
+                case 1 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:256:3: ( '.' ( DECIMAL_DIGIT )+ ( EXPONENT_PART )? ( REAL_TYPE_SUFFIX )? )
+                    {
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:256:3: ( '.' ( DECIMAL_DIGIT )+ ( EXPONENT_PART )? ( REAL_TYPE_SUFFIX )? )
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:256:4: '.' ( DECIMAL_DIGIT )+ ( EXPONENT_PART )? ( REAL_TYPE_SUFFIX )?
+                    {
+                    match('.'); 
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:256:8: ( DECIMAL_DIGIT )+
+                    int cnt10=0;
+                    loop10:
+                    do {
+                        int alt10=2;
+                        int LA10_0 = input.LA(1);
+
+                        if ( ((LA10_0>='0' && LA10_0<='9')) ) {
+                            alt10=1;
+                        }
+
+
+                        switch (alt10) {
+                    	case 1 :
+                    	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:256:9: DECIMAL_DIGIT
+                    	    {
+                    	    mDECIMAL_DIGIT(); 
+
+                    	    }
+                    	    break;
+
+                    	default :
+                    	    if ( cnt10 >= 1 ) break loop10;
+                                EarlyExitException eee =
+                                    new EarlyExitException(10, input);
+                                throw eee;
+                        }
+                        cnt10++;
+                    } while (true);
+
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:256:25: ( EXPONENT_PART )?
+                    int alt11=2;
+                    int LA11_0 = input.LA(1);
+
+                    if ( (LA11_0=='E'||LA11_0=='e') ) {
+                        alt11=1;
+                    }
+                    switch (alt11) {
+                        case 1 :
+                            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:256:26: EXPONENT_PART
+                            {
+                            mEXPONENT_PART(); 
+
+                            }
+                            break;
+
+                    }
+
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:256:42: ( REAL_TYPE_SUFFIX )?
+                    int alt12=2;
+                    int LA12_0 = input.LA(1);
+
+                    if ( (LA12_0=='D'||LA12_0=='F'||LA12_0=='d'||LA12_0=='f') ) {
+                        alt12=1;
+                    }
+                    switch (alt12) {
+                        case 1 :
+                            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:256:43: REAL_TYPE_SUFFIX
+                            {
+                            mREAL_TYPE_SUFFIX(); 
+
+                            }
+                            break;
+
+                    }
+
+
+                    }
+
+
+                    }
+                    break;
+                case 2 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:257:2: ( ( DECIMAL_DIGIT )+ '.' ( DECIMAL_DIGIT )+ ( EXPONENT_PART )? ( REAL_TYPE_SUFFIX )? )
+                    {
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:257:2: ( ( DECIMAL_DIGIT )+ '.' ( DECIMAL_DIGIT )+ ( EXPONENT_PART )? ( REAL_TYPE_SUFFIX )? )
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:257:3: ( DECIMAL_DIGIT )+ '.' ( DECIMAL_DIGIT )+ ( EXPONENT_PART )? ( REAL_TYPE_SUFFIX )?
+                    {
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:257:3: ( DECIMAL_DIGIT )+
+                    int cnt13=0;
+                    loop13:
+                    do {
+                        int alt13=2;
+                        int LA13_0 = input.LA(1);
+
+                        if ( ((LA13_0>='0' && LA13_0<='9')) ) {
+                            alt13=1;
+                        }
+
+
+                        switch (alt13) {
+                    	case 1 :
+                    	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:257:4: DECIMAL_DIGIT
+                    	    {
+                    	    mDECIMAL_DIGIT(); 
+
+                    	    }
+                    	    break;
+
+                    	default :
+                    	    if ( cnt13 >= 1 ) break loop13;
+                                EarlyExitException eee =
+                                    new EarlyExitException(13, input);
+                                throw eee;
+                        }
+                        cnt13++;
+                    } while (true);
+
+                    match('.'); 
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:257:24: ( DECIMAL_DIGIT )+
+                    int cnt14=0;
+                    loop14:
+                    do {
+                        int alt14=2;
+                        int LA14_0 = input.LA(1);
+
+                        if ( ((LA14_0>='0' && LA14_0<='9')) ) {
+                            alt14=1;
+                        }
+
+
+                        switch (alt14) {
+                    	case 1 :
+                    	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:257:25: DECIMAL_DIGIT
+                    	    {
+                    	    mDECIMAL_DIGIT(); 
+
+                    	    }
+                    	    break;
+
+                    	default :
+                    	    if ( cnt14 >= 1 ) break loop14;
+                                EarlyExitException eee =
+                                    new EarlyExitException(14, input);
+                                throw eee;
+                        }
+                        cnt14++;
+                    } while (true);
+
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:257:41: ( EXPONENT_PART )?
+                    int alt15=2;
+                    int LA15_0 = input.LA(1);
+
+                    if ( (LA15_0=='E'||LA15_0=='e') ) {
+                        alt15=1;
+                    }
+                    switch (alt15) {
+                        case 1 :
+                            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:257:42: EXPONENT_PART
+                            {
+                            mEXPONENT_PART(); 
+
+                            }
+                            break;
+
+                    }
+
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:257:58: ( REAL_TYPE_SUFFIX )?
+                    int alt16=2;
+                    int LA16_0 = input.LA(1);
+
+                    if ( (LA16_0=='D'||LA16_0=='F'||LA16_0=='d'||LA16_0=='f') ) {
+                        alt16=1;
+                    }
+                    switch (alt16) {
+                        case 1 :
+                            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:257:59: REAL_TYPE_SUFFIX
+                            {
+                            mREAL_TYPE_SUFFIX(); 
+
+                            }
+                            break;
+
+                    }
+
+
+                    }
+
+
+                    }
+                    break;
+                case 3 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:258:2: ( ( DECIMAL_DIGIT )+ ( EXPONENT_PART ) ( REAL_TYPE_SUFFIX )? )
+                    {
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:258:2: ( ( DECIMAL_DIGIT )+ ( EXPONENT_PART ) ( REAL_TYPE_SUFFIX )? )
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:258:3: ( DECIMAL_DIGIT )+ ( EXPONENT_PART ) ( REAL_TYPE_SUFFIX )?
+                    {
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:258:3: ( DECIMAL_DIGIT )+
+                    int cnt17=0;
+                    loop17:
+                    do {
+                        int alt17=2;
+                        int LA17_0 = input.LA(1);
+
+                        if ( ((LA17_0>='0' && LA17_0<='9')) ) {
+                            alt17=1;
+                        }
+
+
+                        switch (alt17) {
+                    	case 1 :
+                    	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:258:4: DECIMAL_DIGIT
+                    	    {
+                    	    mDECIMAL_DIGIT(); 
+
+                    	    }
+                    	    break;
+
+                    	default :
+                    	    if ( cnt17 >= 1 ) break loop17;
+                                EarlyExitException eee =
+                                    new EarlyExitException(17, input);
+                                throw eee;
+                        }
+                        cnt17++;
+                    } while (true);
+
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:258:20: ( EXPONENT_PART )
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:258:21: EXPONENT_PART
+                    {
+                    mEXPONENT_PART(); 
+
+                    }
+
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:258:36: ( REAL_TYPE_SUFFIX )?
+                    int alt18=2;
+                    int LA18_0 = input.LA(1);
+
+                    if ( (LA18_0=='D'||LA18_0=='F'||LA18_0=='d'||LA18_0=='f') ) {
+                        alt18=1;
+                    }
+                    switch (alt18) {
+                        case 1 :
+                            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:258:37: REAL_TYPE_SUFFIX
+                            {
+                            mREAL_TYPE_SUFFIX(); 
+
+                            }
+                            break;
+
+                    }
+
+
+                    }
+
+
+                    }
+                    break;
+                case 4 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:259:2: ( ( DECIMAL_DIGIT )+ ( REAL_TYPE_SUFFIX ) )
+                    {
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:259:2: ( ( DECIMAL_DIGIT )+ ( REAL_TYPE_SUFFIX ) )
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:259:3: ( DECIMAL_DIGIT )+ ( REAL_TYPE_SUFFIX )
+                    {
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:259:3: ( DECIMAL_DIGIT )+
+                    int cnt19=0;
+                    loop19:
+                    do {
+                        int alt19=2;
+                        int LA19_0 = input.LA(1);
+
+                        if ( ((LA19_0>='0' && LA19_0<='9')) ) {
+                            alt19=1;
+                        }
+
+
+                        switch (alt19) {
+                    	case 1 :
+                    	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:259:4: DECIMAL_DIGIT
+                    	    {
+                    	    mDECIMAL_DIGIT(); 
+
+                    	    }
+                    	    break;
+
+                    	default :
+                    	    if ( cnt19 >= 1 ) break loop19;
+                                EarlyExitException eee =
+                                    new EarlyExitException(19, input);
+                                throw eee;
+                        }
+                        cnt19++;
+                    } while (true);
+
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:259:20: ( REAL_TYPE_SUFFIX )
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:259:21: REAL_TYPE_SUFFIX
+                    {
+                    mREAL_TYPE_SUFFIX(); 
+
+                    }
+
+
+                    }
+
+
+                    }
+                    break;
+
+            }
+            this.type = _type;
+        }
+        finally {
+        }
+    }
+    // $ANTLR end REAL_LITERAL
+
+    // $ANTLR start APOS
+    public final void mAPOS() throws RecognitionException {
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:261:15: ( '\\'' '\\'' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:261:17: '\\'' '\\''
+            {
+            match('\''); 
+            match('\''); 
+
+            }
+
+        }
+        finally {
+        }
+    }
+    // $ANTLR end APOS
+
+    // $ANTLR start DECIMAL_DIGIT
+    public final void mDECIMAL_DIGIT() throws RecognitionException {
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:262:24: ( '0' .. '9' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:262:26: '0' .. '9'
+            {
+            matchRange('0','9'); 
+
+            }
+
+        }
+        finally {
+        }
+    }
+    // $ANTLR end DECIMAL_DIGIT
+
+    // $ANTLR start INTEGER_TYPE_SUFFIX
+    public final void mINTEGER_TYPE_SUFFIX() throws RecognitionException {
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:263:30: ( ( 'L' | 'l' ) )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:263:32: ( 'L' | 'l' )
+            {
+            if ( input.LA(1)=='L'||input.LA(1)=='l' ) {
+                input.consume();
+
+            }
+            else {
+                MismatchedSetException mse =
+                    new MismatchedSetException(null,input);
+                recover(mse);    throw mse;
+            }
+
+
+            }
+
+        }
+        finally {
+        }
+    }
+    // $ANTLR end INTEGER_TYPE_SUFFIX
+
+    // $ANTLR start HEX_DIGIT
+    public final void mHEX_DIGIT() throws RecognitionException {
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:264:20: ( '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'a' | 'b' | 'c' | 'd' | 'e' | 'f' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:
+            {
+            if ( (input.LA(1)>='0' && input.LA(1)<='9')||(input.LA(1)>='A' && input.LA(1)<='F')||(input.LA(1)>='a' && input.LA(1)<='f') ) {
+                input.consume();
+
+            }
+            else {
+                MismatchedSetException mse =
+                    new MismatchedSetException(null,input);
+                recover(mse);    throw mse;
+            }
+
+
+            }
+
+        }
+        finally {
+        }
+    }
+    // $ANTLR end HEX_DIGIT
+
+    // $ANTLR start EXPONENT_PART
+    public final void mEXPONENT_PART() throws RecognitionException {
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:266:24: ( 'e' ( SIGN )* ( DECIMAL_DIGIT )+ | 'E' ( SIGN )* ( DECIMAL_DIGIT )+ )
+            int alt25=2;
+            int LA25_0 = input.LA(1);
+
+            if ( (LA25_0=='e') ) {
+                alt25=1;
+            }
+            else if ( (LA25_0=='E') ) {
+                alt25=2;
+            }
+            else {
+                NoViableAltException nvae =
+                    new NoViableAltException("266:10: fragment EXPONENT_PART : ( 'e' ( SIGN )* ( DECIMAL_DIGIT )+ | 'E' ( SIGN )* ( DECIMAL_DIGIT )+ );", 25, 0, input);
+
+                throw nvae;
+            }
+            switch (alt25) {
+                case 1 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:266:26: 'e' ( SIGN )* ( DECIMAL_DIGIT )+
+                    {
+                    match('e'); 
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:266:31: ( SIGN )*
+                    loop21:
+                    do {
+                        int alt21=2;
+                        int LA21_0 = input.LA(1);
+
+                        if ( (LA21_0=='+'||LA21_0=='-') ) {
+                            alt21=1;
+                        }
+
+
+                        switch (alt21) {
+                    	case 1 :
+                    	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:266:32: SIGN
+                    	    {
+                    	    mSIGN(); 
+
+                    	    }
+                    	    break;
+
+                    	default :
+                    	    break loop21;
+                        }
+                    } while (true);
+
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:266:40: ( DECIMAL_DIGIT )+
+                    int cnt22=0;
+                    loop22:
+                    do {
+                        int alt22=2;
+                        int LA22_0 = input.LA(1);
+
+                        if ( ((LA22_0>='0' && LA22_0<='9')) ) {
+                            alt22=1;
+                        }
+
+
+                        switch (alt22) {
+                    	case 1 :
+                    	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:266:41: DECIMAL_DIGIT
+                    	    {
+                    	    mDECIMAL_DIGIT(); 
+
+                    	    }
+                    	    break;
+
+                    	default :
+                    	    if ( cnt22 >= 1 ) break loop22;
+                                EarlyExitException eee =
+                                    new EarlyExitException(22, input);
+                                throw eee;
+                        }
+                        cnt22++;
+                    } while (true);
+
+
+                    }
+                    break;
+                case 2 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:266:59: 'E' ( SIGN )* ( DECIMAL_DIGIT )+
+                    {
+                    match('E'); 
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:266:64: ( SIGN )*
+                    loop23:
+                    do {
+                        int alt23=2;
+                        int LA23_0 = input.LA(1);
+
+                        if ( (LA23_0=='+'||LA23_0=='-') ) {
+                            alt23=1;
+                        }
+
+
+                        switch (alt23) {
+                    	case 1 :
+                    	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:266:65: SIGN
+                    	    {
+                    	    mSIGN(); 
+
+                    	    }
+                    	    break;
+
+                    	default :
+                    	    break loop23;
+                        }
+                    } while (true);
+
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:266:73: ( DECIMAL_DIGIT )+
+                    int cnt24=0;
+                    loop24:
+                    do {
+                        int alt24=2;
+                        int LA24_0 = input.LA(1);
+
+                        if ( ((LA24_0>='0' && LA24_0<='9')) ) {
+                            alt24=1;
+                        }
+
+
+                        switch (alt24) {
+                    	case 1 :
+                    	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:266:74: DECIMAL_DIGIT
+                    	    {
+                    	    mDECIMAL_DIGIT(); 
+
+                    	    }
+                    	    break;
+
+                    	default :
+                    	    if ( cnt24 >= 1 ) break loop24;
+                                EarlyExitException eee =
+                                    new EarlyExitException(24, input);
+                                throw eee;
+                        }
+                        cnt24++;
+                    } while (true);
+
+
+                    }
+                    break;
+
+            }
+        }
+        finally {
+        }
+    }
+    // $ANTLR end EXPONENT_PART
+
+    // $ANTLR start SIGN
+    public final void mSIGN() throws RecognitionException {
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:267:15: ( '+' | '-' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:
+            {
+            if ( input.LA(1)=='+'||input.LA(1)=='-' ) {
+                input.consume();
+
+            }
+            else {
+                MismatchedSetException mse =
+                    new MismatchedSetException(null,input);
+                recover(mse);    throw mse;
+            }
+
+
+            }
+
+        }
+        finally {
+        }
+    }
+    // $ANTLR end SIGN
+
+    // $ANTLR start REAL_TYPE_SUFFIX
+    public final void mREAL_TYPE_SUFFIX() throws RecognitionException {
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:268:27: ( 'F' | 'f' | 'D' | 'd' )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:
+            {
+            if ( input.LA(1)=='D'||input.LA(1)=='F'||input.LA(1)=='d'||input.LA(1)=='f' ) {
+                input.consume();
+
+            }
+            else {
+                MismatchedSetException mse =
+                    new MismatchedSetException(null,input);
+                recover(mse);    throw mse;
+            }
+
+
+            }
+
+        }
+        finally {
+        }
+    }
+    // $ANTLR end REAL_TYPE_SUFFIX
+
+    public void mTokens() throws RecognitionException {
+        // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:8: ( T77 | INTEGER_LITERAL | HEXADECIMAL_INTEGER_LITERAL | ASSIGN | EQUAL | NOT_EQUAL | LESS_THAN | LESS_THAN_OR_EQUAL | GREATER_THAN | GREATER_THAN_OR_EQUAL | INSTANCEOF | BETWEEN | MATCHES | NULL_LITERAL | SEMI | DOT | COMMA | LPAREN | RPAREN | LCURLY | RCURLY | LBRACKET | RBRACKET | PIPE | AND | OR | FALSE | TRUE | PLUS | MINUS | DIV | STAR | MOD | POWER | BANG | POUND | QMARK | DEFAULT | PROJECT | SELECT | SELECT_FIRST | SELECT_LAST | TYPE | STRING_LITERAL | DQ_STRING_LITERAL | ID | DOT_ESCAPED | WS | DOLLAR | AT | UPTO | COLON | REAL_LITERAL )
+        int alt26=53;
+        alt26 = dfa26.predict(input);
+        switch (alt26) {
+            case 1 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:10: T77
+                {
+                mT77(); 
+
+                }
+                break;
+            case 2 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:14: INTEGER_LITERAL
+                {
+                mINTEGER_LITERAL(); 
+
+                }
+                break;
+            case 3 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:30: HEXADECIMAL_INTEGER_LITERAL
+                {
+                mHEXADECIMAL_INTEGER_LITERAL(); 
+
+                }
+                break;
+            case 4 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:58: ASSIGN
+                {
+                mASSIGN(); 
+
+                }
+                break;
+            case 5 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:65: EQUAL
+                {
+                mEQUAL(); 
+
+                }
+                break;
+            case 6 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:71: NOT_EQUAL
+                {
+                mNOT_EQUAL(); 
+
+                }
+                break;
+            case 7 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:81: LESS_THAN
+                {
+                mLESS_THAN(); 
+
+                }
+                break;
+            case 8 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:91: LESS_THAN_OR_EQUAL
+                {
+                mLESS_THAN_OR_EQUAL(); 
+
+                }
+                break;
+            case 9 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:110: GREATER_THAN
+                {
+                mGREATER_THAN(); 
+
+                }
+                break;
+            case 10 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:123: GREATER_THAN_OR_EQUAL
+                {
+                mGREATER_THAN_OR_EQUAL(); 
+
+                }
+                break;
+            case 11 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:145: INSTANCEOF
+                {
+                mINSTANCEOF(); 
+
+                }
+                break;
+            case 12 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:156: BETWEEN
+                {
+                mBETWEEN(); 
+
+                }
+                break;
+            case 13 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:164: MATCHES
+                {
+                mMATCHES(); 
+
+                }
+                break;
+            case 14 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:172: NULL_LITERAL
+                {
+                mNULL_LITERAL(); 
+
+                }
+                break;
+            case 15 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:185: SEMI
+                {
+                mSEMI(); 
+
+                }
+                break;
+            case 16 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:190: DOT
+                {
+                mDOT(); 
+
+                }
+                break;
+            case 17 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:194: COMMA
+                {
+                mCOMMA(); 
+
+                }
+                break;
+            case 18 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:200: LPAREN
+                {
+                mLPAREN(); 
+
+                }
+                break;
+            case 19 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:207: RPAREN
+                {
+                mRPAREN(); 
+
+                }
+                break;
+            case 20 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:214: LCURLY
+                {
+                mLCURLY(); 
+
+                }
+                break;
+            case 21 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:221: RCURLY
+                {
+                mRCURLY(); 
+
+                }
+                break;
+            case 22 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:228: LBRACKET
+                {
+                mLBRACKET(); 
+
+                }
+                break;
+            case 23 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:237: RBRACKET
+                {
+                mRBRACKET(); 
+
+                }
+                break;
+            case 24 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:246: PIPE
+                {
+                mPIPE(); 
+
+                }
+                break;
+            case 25 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:251: AND
+                {
+                mAND(); 
+
+                }
+                break;
+            case 26 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:255: OR
+                {
+                mOR(); 
+
+                }
+                break;
+            case 27 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:258: FALSE
+                {
+                mFALSE(); 
+
+                }
+                break;
+            case 28 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:264: TRUE
+                {
+                mTRUE(); 
+
+                }
+                break;
+            case 29 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:269: PLUS
+                {
+                mPLUS(); 
+
+                }
+                break;
+            case 30 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:274: MINUS
+                {
+                mMINUS(); 
+
+                }
+                break;
+            case 31 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:280: DIV
+                {
+                mDIV(); 
+
+                }
+                break;
+            case 32 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:284: STAR
+                {
+                mSTAR(); 
+
+                }
+                break;
+            case 33 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:289: MOD
+                {
+                mMOD(); 
+
+                }
+                break;
+            case 34 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:293: POWER
+                {
+                mPOWER(); 
+
+                }
+                break;
+            case 35 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:299: BANG
+                {
+                mBANG(); 
+
+                }
+                break;
+            case 36 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:304: POUND
+                {
+                mPOUND(); 
+
+                }
+                break;
+            case 37 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:310: QMARK
+                {
+                mQMARK(); 
+
+                }
+                break;
+            case 38 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:316: DEFAULT
+                {
+                mDEFAULT(); 
+
+                }
+                break;
+            case 39 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:324: PROJECT
+                {
+                mPROJECT(); 
+
+                }
+                break;
+            case 40 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:332: SELECT
+                {
+                mSELECT(); 
+
+                }
+                break;
+            case 41 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:339: SELECT_FIRST
+                {
+                mSELECT_FIRST(); 
+
+                }
+                break;
+            case 42 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:352: SELECT_LAST
+                {
+                mSELECT_LAST(); 
+
+                }
+                break;
+            case 43 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:364: TYPE
+                {
+                mTYPE(); 
+
+                }
+                break;
+            case 44 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:369: STRING_LITERAL
+                {
+                mSTRING_LITERAL(); 
+
+                }
+                break;
+            case 45 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:384: DQ_STRING_LITERAL
+                {
+                mDQ_STRING_LITERAL(); 
+
+                }
+                break;
+            case 46 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:402: ID
+                {
+                mID(); 
+
+                }
+                break;
+            case 47 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:405: DOT_ESCAPED
+                {
+                mDOT_ESCAPED(); 
+
+                }
+                break;
+            case 48 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:417: WS
+                {
+                mWS(); 
+
+                }
+                break;
+            case 49 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:420: DOLLAR
+                {
+                mDOLLAR(); 
+
+                }
+                break;
+            case 50 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:427: AT
+                {
+                mAT(); 
+
+                }
+                break;
+            case 51 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:430: UPTO
+                {
+                mUPTO(); 
+
+                }
+                break;
+            case 52 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:435: COLON
+                {
+                mCOLON(); 
+
+                }
+                break;
+            case 53 :
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:1:441: REAL_LITERAL
+                {
+                mREAL_LITERAL(); 
+
+                }
+                break;
+
+        }
+
+    }
+
+
+    protected DFA20 dfa20 = new DFA20(this);
+    protected DFA26 dfa26 = new DFA26(this);
+    static final String DFA20_eotS =
+        "\6\uffff";
+    static final String DFA20_eofS =
+        "\6\uffff";
+    static final String DFA20_minS =
+        "\1\56\1\uffff\1\56\3\uffff";
+    static final String DFA20_maxS =
+        "\1\71\1\uffff\1\146\3\uffff";
+    static final String DFA20_acceptS =
+        "\1\uffff\1\1\1\uffff\1\4\1\3\1\2";
+    static final String DFA20_specialS =
+        "\6\uffff}>";
+    static final String[] DFA20_transitionS = {
+            "\1\1\1\uffff\12\2",
+            "",
+            "\1\5\1\uffff\12\2\12\uffff\1\3\1\4\1\3\35\uffff\1\3\1\4\1\3",
+            "",
+            "",
+            ""
+    };
+
+    static final short[] DFA20_eot = DFA.unpackEncodedString(DFA20_eotS);
+    static final short[] DFA20_eof = DFA.unpackEncodedString(DFA20_eofS);
+    static final char[] DFA20_min = DFA.unpackEncodedStringToUnsignedChars(DFA20_minS);
+    static final char[] DFA20_max = DFA.unpackEncodedStringToUnsignedChars(DFA20_maxS);
+    static final short[] DFA20_accept = DFA.unpackEncodedString(DFA20_acceptS);
+    static final short[] DFA20_special = DFA.unpackEncodedString(DFA20_specialS);
+    static final short[][] DFA20_transition;
+
+    static {
+        int numStates = DFA20_transitionS.length;
+        DFA20_transition = new short[numStates][];
+        for (int i=0; i<numStates; i++) {
+            DFA20_transition[i] = DFA.unpackEncodedString(DFA20_transitionS[i]);
+        }
+    }
+
+    class DFA20 extends DFA {
+
+        public DFA20(BaseRecognizer recognizer) {
+            this.recognizer = recognizer;
+            this.decisionNumber = 20;
+            this.eot = DFA20_eot;
+            this.eof = DFA20_eof;
+            this.min = DFA20_min;
+            this.max = DFA20_max;
+            this.accept = DFA20_accept;
+            this.special = DFA20_special;
+            this.transition = DFA20_transition;
+        }
+        public String getDescription() {
+            return "255:1: REAL_LITERAL : ( ( '.' ( DECIMAL_DIGIT )+ ( EXPONENT_PART )? ( REAL_TYPE_SUFFIX )? ) | ( ( DECIMAL_DIGIT )+ '.' ( DECIMAL_DIGIT )+ ( EXPONENT_PART )? ( REAL_TYPE_SUFFIX )? ) | ( ( DECIMAL_DIGIT )+ ( EXPONENT_PART ) ( REAL_TYPE_SUFFIX )? ) | ( ( DECIMAL_DIGIT )+ ( REAL_TYPE_SUFFIX ) ) );";
+        }
+    }
+    static final String DFA26_eotS =
+        "\1\uffff\1\45\2\55\1\60\1\63\1\65\1\67\3\45\1\uffff\1\74\10\uffff"+
+        "\4\45\5\uffff\1\102\1\uffff\1\105\1\107\1\45\7\uffff\2\45\14\uffff"+
+        "\3\45\2\uffff\1\45\1\117\2\45\10\uffff\1\122\4\45\1\127\1\uffff"+
+        "\2\45\1\uffff\1\132\3\45\1\uffff\1\45\1\137\1\uffff\3\45\1\143\1"+
+        "\uffff\3\45\1\uffff\1\45\1\150\1\151\1\45\2\uffff\1\45\1\154\1\uffff";
+    static final String DFA26_eofS =
+        "\155\uffff";
+    static final String DFA26_minS =
+        "\1\11\1\145\2\56\4\75\1\156\1\145\1\141\1\uffff\1\56\10\uffff\1"+
+        "\156\1\162\1\141\1\162\5\uffff\1\173\1\uffff\1\77\1\173\1\50\7\uffff"+
+        "\1\167\1\154\14\uffff\1\163\2\164\2\uffff\1\144\1\60\1\154\1\165"+
+        "\10\uffff\1\60\1\154\1\164\1\167\1\143\1\60\1\uffff\1\163\1\145"+
+        "\1\uffff\1\60\1\141\1\145\1\150\1\uffff\1\145\1\60\1\uffff\1\156"+
+        "\2\145\1\60\1\uffff\1\143\1\156\1\163\1\uffff\1\145\2\60\1\157\2"+
+        "\uffff\1\146\1\60\1\uffff";
+    static final String DFA26_maxS =
+        "\1\175\1\165\1\170\1\146\1\75\1\173\2\75\1\156\1\145\1\141\1\uffff"+
+        "\1\71\10\uffff\1\156\1\162\1\141\1\162\5\uffff\1\173\1\uffff\2\173"+
+        "\1\50\7\uffff\1\167\1\154\14\uffff\1\163\2\164\2\uffff\1\144\1\172"+
+        "\1\154\1\165\10\uffff\1\172\1\154\1\164\1\167\1\143\1\172\1\uffff"+
+        "\1\163\1\145\1\uffff\1\172\1\141\1\145\1\150\1\uffff\1\145\1\172"+
+        "\1\uffff\1\156\2\145\1\172\1\uffff\1\143\1\156\1\163\1\uffff\1\145"+
+        "\2\172\1\157\2\uffff\1\146\1\172\1\uffff";
+    static final String DFA26_acceptS =
+        "\13\uffff\1\17\1\uffff\1\21\1\22\1\23\1\24\1\25\1\26\1\27\1\30\4"+
+        "\uffff\1\35\1\36\1\37\1\40\1\41\1\uffff\1\44\3\uffff\1\54\1\55\1"+
+        "\56\1\57\1\60\1\62\1\64\2\uffff\1\3\1\2\1\65\1\5\1\4\1\47\1\6\1"+
+        "\43\1\10\1\7\1\12\1\11\3\uffff\1\63\1\20\4\uffff\1\51\1\42\1\46"+
+        "\1\50\1\45\1\52\1\61\1\53\6\uffff\1\32\2\uffff\1\1\4\uffff\1\31"+
+        "\2\uffff\1\16\4\uffff\1\34\3\uffff\1\33\4\uffff\1\14\1\15\2\uffff"+
+        "\1\13";
+    static final String DFA26_specialS =
+        "\155\uffff}>";
+    static final String[] DFA26_transitionS = {
+            "\2\47\2\uffff\1\47\22\uffff\1\47\1\5\1\44\1\37\1\41\1\35\1\uffff"+
+            "\1\43\1\16\1\17\1\34\1\31\1\15\1\32\1\14\1\33\1\2\11\3\1\51"+
+            "\1\13\1\6\1\4\1\7\1\40\1\50\23\45\1\42\6\45\1\22\1\46\1\23\1"+
+            "\36\1\45\1\uffff\1\25\1\11\3\45\1\27\2\45\1\10\3\45\1\12\1\1"+
+            "\1\26\4\45\1\30\6\45\1\20\1\24\1\21",
+            "\1\52\17\uffff\1\53",
+            "\1\56\1\uffff\12\3\12\uffff\3\56\21\uffff\1\54\13\uffff\3\56"+
+            "\21\uffff\1\54",
+            "\1\56\1\uffff\12\3\12\uffff\3\56\35\uffff\3\56",
+            "\1\57",
+            "\1\62\75\uffff\1\61",
+            "\1\64",
+            "\1\66",
+            "\1\70",
+            "\1\71",
+            "\1\72",
+            "",
+            "\1\73\1\uffff\12\56",
+            "",
+            "",
+            "",
+            "",
+            "",
+            "",
+            "",
+            "",
+            "\1\75",
+            "\1\76",
+            "\1\77",
+            "\1\100",
+            "",
+            "",
+            "",
+            "",
+            "",
+            "\1\101",
+            "",
+            "\1\103\73\uffff\1\104",
+            "\1\106",
+            "\1\110",
+            "",
+            "",
+            "",
+            "",
+            "",
+            "",
+            "",
+            "\1\111",
+            "\1\112",
+            "",
+            "",
+            "",
+            "",
+            "",
+            "",
+            "",
+            "",
+            "",
+            "",
+            "",
+            "",
+            "\1\113",
+            "\1\114",
+            "\1\115",
+            "",
+            "",
+            "\1\116",
+            "\12\45\7\uffff\32\45\1\uffff\1\45\2\uffff\1\45\1\uffff\32\45",
+            "\1\120",
+            "\1\121",
+            "",
+            "",
+            "",
+            "",
+            "",
+            "",
+            "",
+            "",
+            "\12\45\7\uffff\32\45\1\uffff\1\45\2\uffff\1\45\1\uffff\32\45",
+            "\1\123",
+            "\1\124",
+            "\1\125",
+            "\1\126",
+            "\12\45\7\uffff\32\45\1\uffff\1\45\2\uffff\1\45\1\uffff\32\45",
+            "",
+            "\1\130",
+            "\1\131",
+            "",
+            "\12\45\7\uffff\32\45\1\uffff\1\45\2\uffff\1\45\1\uffff\32\45",
+            "\1\133",
+            "\1\134",
+            "\1\135",
+            "",
+            "\1\136",
+            "\12\45\7\uffff\32\45\1\uffff\1\45\2\uffff\1\45\1\uffff\32\45",
+            "",
+            "\1\140",
+            "\1\141",
+            "\1\142",
+            "\12\45\7\uffff\32\45\1\uffff\1\45\2\uffff\1\45\1\uffff\32\45",
+            "",
+            "\1\144",
+            "\1\145",
+            "\1\146",
+            "",
+            "\1\147",
+            "\12\45\7\uffff\32\45\1\uffff\1\45\2\uffff\1\45\1\uffff\32\45",
+            "\12\45\7\uffff\32\45\1\uffff\1\45\2\uffff\1\45\1\uffff\32\45",
+            "\1\152",
+            "",
+            "",
+            "\1\153",
+            "\12\45\7\uffff\32\45\1\uffff\1\45\2\uffff\1\45\1\uffff\32\45",
+            ""
+    };
+
+    static final short[] DFA26_eot = DFA.unpackEncodedString(DFA26_eotS);
+    static final short[] DFA26_eof = DFA.unpackEncodedString(DFA26_eofS);
+    static final char[] DFA26_min = DFA.unpackEncodedStringToUnsignedChars(DFA26_minS);
+    static final char[] DFA26_max = DFA.unpackEncodedStringToUnsignedChars(DFA26_maxS);
+    static final short[] DFA26_accept = DFA.unpackEncodedString(DFA26_acceptS);
+    static final short[] DFA26_special = DFA.unpackEncodedString(DFA26_specialS);
+    static final short[][] DFA26_transition;
+
+    static {
+        int numStates = DFA26_transitionS.length;
+        DFA26_transition = new short[numStates][];
+        for (int i=0; i<numStates; i++) {
+            DFA26_transition[i] = DFA.unpackEncodedString(DFA26_transitionS[i]);
+        }
+    }
+
+    class DFA26 extends DFA {
+
+        public DFA26(BaseRecognizer recognizer) {
+            this.recognizer = recognizer;
+            this.decisionNumber = 26;
+            this.eot = DFA26_eot;
+            this.eof = DFA26_eof;
+            this.min = DFA26_min;
+            this.max = DFA26_max;
+            this.accept = DFA26_accept;
+            this.special = DFA26_special;
+            this.transition = DFA26_transition;
+        }
+        public String getDescription() {
+            return "1:1: Tokens : ( T77 | INTEGER_LITERAL | HEXADECIMAL_INTEGER_LITERAL | ASSIGN | EQUAL | NOT_EQUAL | LESS_THAN | LESS_THAN_OR_EQUAL | GREATER_THAN | GREATER_THAN_OR_EQUAL | INSTANCEOF | BETWEEN | MATCHES | NULL_LITERAL | SEMI | DOT | COMMA | LPAREN | RPAREN | LCURLY | RCURLY | LBRACKET | RBRACKET | PIPE | AND | OR | FALSE | TRUE | PLUS | MINUS | DIV | STAR | MOD | POWER | BANG | POUND | QMARK | DEFAULT | PROJECT | SELECT | SELECT_FIRST | SELECT_LAST | TYPE | STRING_LITERAL | DQ_STRING_LITERAL | ID | DOT_ESCAPED | WS | DOLLAR | AT | UPTO | COLON | REAL_LITERAL );";
+        }
+    }
+ 
+
+}

+ 4105 - 0
spring-el/src/main/java/org/springframework/expression/spel/generated/SpringExpressionsParser.java

@@ -0,0 +1,4105 @@
+// $ANTLR 3.0.1 /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g 2008-09-16 19:06:07
+package org.springframework.expression.spel.generated;
+
+import org.antlr.runtime.*;
+import java.util.Stack;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashMap;
+
+import org.antlr.runtime.tree.*;
+
+public class SpringExpressionsParser extends Parser {
+    public static final String[] tokenNames = new String[] {
+        "<invalid>", "<EOR>", "<DOWN>", "<UP>", "INTEGER_LITERAL", "EXPRESSION", "QUALIFIED_IDENTIFIER", "PROPERTY_OR_FIELD", "INDEXER", "CONSTRUCTOR", "HOLDER", "NAMED_ARGUMENT", "FUNCTIONREF", "TYPEREF", "VARIABLEREF", "METHOD", "ADD", "SUBTRACT", "NUMBER", "ASSIGN", "DEFAULT", "QMARK", "COLON", "LPAREN", "RPAREN", "OR", "AND", "PLUS", "MINUS", "STAR", "DIV", "MOD", "POWER", "BANG", "DOT", "POUND", "ID", "COMMA", "LBRACKET", "RBRACKET", "PROJECT", "RCURLY", "SELECT", "SELECT_FIRST", "SELECT_LAST", "TYPE", "STRING_LITERAL", "DQ_STRING_LITERAL", "NULL_LITERAL", "HEXADECIMAL_INTEGER_LITERAL", "REAL_LITERAL", "TRUE", "FALSE", "DECIMAL_DIGIT", "INTEGER_TYPE_SUFFIX", "HEX_DIGIT", "EQUAL", "NOT_EQUAL", "LESS_THAN", "LESS_THAN_OR_EQUAL", "GREATER_THAN", "GREATER_THAN_OR_EQUAL", "INSTANCEOF", "BETWEEN", "MATCHES", "SEMI", "LCURLY", "PIPE", "APOS", "DOT_ESCAPED", "WS", "DOLLAR", "AT", "UPTO", "EXPONENT_PART", "REAL_TYPE_SUFFIX", "SIGN", "'new'"
+    };
+    public static final int GREATER_THAN_OR_EQUAL=61;
+    public static final int HOLDER=10;
+    public static final int COMMA=37;
+    public static final int SELECT_FIRST=43;
+    public static final int GREATER_THAN=60;
+    public static final int TYPE=45;
+    public static final int MINUS=28;
+    public static final int SELECT_LAST=44;
+    public static final int NUMBER=18;
+    public static final int LESS_THAN=58;
+    public static final int BANG=33;
+    public static final int FALSE=52;
+    public static final int METHOD=15;
+    public static final int PROPERTY_OR_FIELD=7;
+    public static final int LBRACKET=38;
+    public static final int INDEXER=8;
+    public static final int MOD=31;
+    public static final int FUNCTIONREF=12;
+    public static final int NULL_LITERAL=48;
+    public static final int NAMED_ARGUMENT=11;
+    public static final int OR=25;
+    public static final int PIPE=67;
+    public static final int DOT=34;
+    public static final int RCURLY=41;
+    public static final int EXPRESSION=5;
+    public static final int AND=26;
+    public static final int LCURLY=66;
+    public static final int REAL_TYPE_SUFFIX=75;
+    public static final int STRING_LITERAL=46;
+    public static final int QUALIFIED_IDENTIFIER=6;
+    public static final int SELECT=42;
+    public static final int ASSIGN=19;
+    public static final int SUBTRACT=17;
+    public static final int RBRACKET=39;
+    public static final int INSTANCEOF=62;
+    public static final int BETWEEN=63;
+    public static final int RPAREN=24;
+    public static final int SIGN=76;
+    public static final int LPAREN=23;
+    public static final int HEX_DIGIT=55;
+    public static final int PLUS=27;
+    public static final int APOS=68;
+    public static final int INTEGER_LITERAL=4;
+    public static final int AT=72;
+    public static final int ID=36;
+    public static final int NOT_EQUAL=57;
+    public static final int POWER=32;
+    public static final int TYPEREF=13;
+    public static final int DECIMAL_DIGIT=53;
+    public static final int WS=70;
+    public static final int DOLLAR=71;
+    public static final int LESS_THAN_OR_EQUAL=59;
+    public static final int DQ_STRING_LITERAL=47;
+    public static final int HEXADECIMAL_INTEGER_LITERAL=49;
+    public static final int SEMI=65;
+    public static final int CONSTRUCTOR=9;
+    public static final int INTEGER_TYPE_SUFFIX=54;
+    public static final int EQUAL=56;
+    public static final int MATCHES=64;
+    public static final int DOT_ESCAPED=69;
+    public static final int UPTO=73;
+    public static final int EOF=-1;
+    public static final int QMARK=21;
+    public static final int DEFAULT=20;
+    public static final int COLON=22;
+    public static final int PROJECT=40;
+    public static final int DIV=30;
+    public static final int STAR=29;
+    public static final int REAL_LITERAL=50;
+    public static final int VARIABLEREF=14;
+    public static final int EXPONENT_PART=74;
+    public static final int TRUE=51;
+    public static final int ADD=16;
+    public static final int POUND=35;
+
+        public SpringExpressionsParser(TokenStream input) {
+            super(input);
+            ruleMemo = new HashMap[40+1];
+         }
+        
+    protected TreeAdaptor adaptor = new CommonTreeAdaptor();
+
+    public void setTreeAdaptor(TreeAdaptor adaptor) {
+        this.adaptor = adaptor;
+    }
+    public TreeAdaptor getTreeAdaptor() {
+        return adaptor;
+    }
+
+    public String[] getTokenNames() { return tokenNames; }
+    public String getGrammarFileName() { return "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g"; }
+
+
+      // For collecting info whilst processing rules that can be used in messages
+      protected Stack<String> paraphrase = new Stack<String>();
+
+
+    public static class expr_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start expr
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:45:1: expr : expression EOF ;
+    public final expr_return expr() throws RecognitionException {
+        expr_return retval = new expr_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token EOF2=null;
+        expression_return expression1 = null;
+
+
+        Object EOF2_tree=null;
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:45:5: ( expression EOF )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:45:7: expression EOF
+            {
+            root_0 = (Object)adaptor.nil();
+
+            pushFollow(FOLLOW_expression_in_expr130);
+            expression1=expression();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) adaptor.addChild(root_0, expression1.getTree());
+            EOF2=(Token)input.LT(1);
+            match(input,EOF,FOLLOW_EOF_in_expr132); if (failed) return retval;
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end expr
+
+    public static class expression_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start expression
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:47:1: expression : logicalOrExpression ( ( ASSIGN logicalOrExpression ) | ( DEFAULT logicalOrExpression ) | ( QMARK expression COLON expression ) )? ;
+    public final expression_return expression() throws RecognitionException {
+        expression_return retval = new expression_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token ASSIGN4=null;
+        Token DEFAULT6=null;
+        Token QMARK8=null;
+        Token COLON10=null;
+        logicalOrExpression_return logicalOrExpression3 = null;
+
+        logicalOrExpression_return logicalOrExpression5 = null;
+
+        logicalOrExpression_return logicalOrExpression7 = null;
+
+        expression_return expression9 = null;
+
+        expression_return expression11 = null;
+
+
+        Object ASSIGN4_tree=null;
+        Object DEFAULT6_tree=null;
+        Object QMARK8_tree=null;
+        Object COLON10_tree=null;
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:47:12: ( logicalOrExpression ( ( ASSIGN logicalOrExpression ) | ( DEFAULT logicalOrExpression ) | ( QMARK expression COLON expression ) )? )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:48:5: logicalOrExpression ( ( ASSIGN logicalOrExpression ) | ( DEFAULT logicalOrExpression ) | ( QMARK expression COLON expression ) )?
+            {
+            root_0 = (Object)adaptor.nil();
+
+            pushFollow(FOLLOW_logicalOrExpression_in_expression152);
+            logicalOrExpression3=logicalOrExpression();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) adaptor.addChild(root_0, logicalOrExpression3.getTree());
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:49:5: ( ( ASSIGN logicalOrExpression ) | ( DEFAULT logicalOrExpression ) | ( QMARK expression COLON expression ) )?
+            int alt1=4;
+            switch ( input.LA(1) ) {
+                case ASSIGN:
+                    {
+                    alt1=1;
+                    }
+                    break;
+                case DEFAULT:
+                    {
+                    alt1=2;
+                    }
+                    break;
+                case QMARK:
+                    {
+                    alt1=3;
+                    }
+                    break;
+            }
+
+            switch (alt1) {
+                case 1 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:49:7: ( ASSIGN logicalOrExpression )
+                    {
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:49:7: ( ASSIGN logicalOrExpression )
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:49:8: ASSIGN logicalOrExpression
+                    {
+                    ASSIGN4=(Token)input.LT(1);
+                    match(input,ASSIGN,FOLLOW_ASSIGN_in_expression161); if (failed) return retval;
+                    if ( backtracking==0 ) {
+                    ASSIGN4_tree = (Object)adaptor.create(ASSIGN4);
+                    root_0 = (Object)adaptor.becomeRoot(ASSIGN4_tree, root_0);
+                    }
+                    pushFollow(FOLLOW_logicalOrExpression_in_expression164);
+                    logicalOrExpression5=logicalOrExpression();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, logicalOrExpression5.getTree());
+
+                    }
+
+
+                    }
+                    break;
+                case 2 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:50:6: ( DEFAULT logicalOrExpression )
+                    {
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:50:6: ( DEFAULT logicalOrExpression )
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:50:7: DEFAULT logicalOrExpression
+                    {
+                    DEFAULT6=(Token)input.LT(1);
+                    match(input,DEFAULT,FOLLOW_DEFAULT_in_expression174); if (failed) return retval;
+                    if ( backtracking==0 ) {
+                    DEFAULT6_tree = (Object)adaptor.create(DEFAULT6);
+                    root_0 = (Object)adaptor.becomeRoot(DEFAULT6_tree, root_0);
+                    }
+                    pushFollow(FOLLOW_logicalOrExpression_in_expression177);
+                    logicalOrExpression7=logicalOrExpression();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, logicalOrExpression7.getTree());
+
+                    }
+
+
+                    }
+                    break;
+                case 3 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:51:6: ( QMARK expression COLON expression )
+                    {
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:51:6: ( QMARK expression COLON expression )
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:51:7: QMARK expression COLON expression
+                    {
+                    QMARK8=(Token)input.LT(1);
+                    match(input,QMARK,FOLLOW_QMARK_in_expression187); if (failed) return retval;
+                    if ( backtracking==0 ) {
+                    QMARK8_tree = (Object)adaptor.create(QMARK8);
+                    root_0 = (Object)adaptor.becomeRoot(QMARK8_tree, root_0);
+                    }
+                    pushFollow(FOLLOW_expression_in_expression190);
+                    expression9=expression();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, expression9.getTree());
+                    COLON10=(Token)input.LT(1);
+                    match(input,COLON,FOLLOW_COLON_in_expression192); if (failed) return retval;
+                    pushFollow(FOLLOW_expression_in_expression195);
+                    expression11=expression();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, expression11.getTree());
+
+                    }
+
+
+                    }
+                    break;
+
+            }
+
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end expression
+
+    public static class parenExpr_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start parenExpr
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:53:1: parenExpr : LPAREN expression RPAREN ;
+    public final parenExpr_return parenExpr() throws RecognitionException {
+        parenExpr_return retval = new parenExpr_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token LPAREN12=null;
+        Token RPAREN14=null;
+        expression_return expression13 = null;
+
+
+        Object LPAREN12_tree=null;
+        Object RPAREN14_tree=null;
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:53:11: ( LPAREN expression RPAREN )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:53:13: LPAREN expression RPAREN
+            {
+            root_0 = (Object)adaptor.nil();
+
+            LPAREN12=(Token)input.LT(1);
+            match(input,LPAREN,FOLLOW_LPAREN_in_parenExpr206); if (failed) return retval;
+            pushFollow(FOLLOW_expression_in_parenExpr209);
+            expression13=expression();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) adaptor.addChild(root_0, expression13.getTree());
+            RPAREN14=(Token)input.LT(1);
+            match(input,RPAREN,FOLLOW_RPAREN_in_parenExpr211); if (failed) return retval;
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end parenExpr
+
+    public static class logicalOrExpression_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start logicalOrExpression
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:55:1: logicalOrExpression : logicalAndExpression ( OR logicalAndExpression )* ;
+    public final logicalOrExpression_return logicalOrExpression() throws RecognitionException {
+        logicalOrExpression_return retval = new logicalOrExpression_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token OR16=null;
+        logicalAndExpression_return logicalAndExpression15 = null;
+
+        logicalAndExpression_return logicalAndExpression17 = null;
+
+
+        Object OR16_tree=null;
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:56:1: ( logicalAndExpression ( OR logicalAndExpression )* )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:56:3: logicalAndExpression ( OR logicalAndExpression )*
+            {
+            root_0 = (Object)adaptor.nil();
+
+            pushFollow(FOLLOW_logicalAndExpression_in_logicalOrExpression222);
+            logicalAndExpression15=logicalAndExpression();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) adaptor.addChild(root_0, logicalAndExpression15.getTree());
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:56:24: ( OR logicalAndExpression )*
+            loop2:
+            do {
+                int alt2=2;
+                int LA2_0 = input.LA(1);
+
+                if ( (LA2_0==OR) ) {
+                    alt2=1;
+                }
+
+
+                switch (alt2) {
+            	case 1 :
+            	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:56:25: OR logicalAndExpression
+            	    {
+            	    OR16=(Token)input.LT(1);
+            	    match(input,OR,FOLLOW_OR_in_logicalOrExpression225); if (failed) return retval;
+            	    if ( backtracking==0 ) {
+            	    OR16_tree = (Object)adaptor.create(OR16);
+            	    root_0 = (Object)adaptor.becomeRoot(OR16_tree, root_0);
+            	    }
+            	    pushFollow(FOLLOW_logicalAndExpression_in_logicalOrExpression228);
+            	    logicalAndExpression17=logicalAndExpression();
+            	    _fsp--;
+            	    if (failed) return retval;
+            	    if ( backtracking==0 ) adaptor.addChild(root_0, logicalAndExpression17.getTree());
+
+            	    }
+            	    break;
+
+            	default :
+            	    break loop2;
+                }
+            } while (true);
+
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end logicalOrExpression
+
+    public static class logicalAndExpression_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start logicalAndExpression
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:58:1: logicalAndExpression : relationalExpression ( AND relationalExpression )* ;
+    public final logicalAndExpression_return logicalAndExpression() throws RecognitionException {
+        logicalAndExpression_return retval = new logicalAndExpression_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token AND19=null;
+        relationalExpression_return relationalExpression18 = null;
+
+        relationalExpression_return relationalExpression20 = null;
+
+
+        Object AND19_tree=null;
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:59:1: ( relationalExpression ( AND relationalExpression )* )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:59:3: relationalExpression ( AND relationalExpression )*
+            {
+            root_0 = (Object)adaptor.nil();
+
+            pushFollow(FOLLOW_relationalExpression_in_logicalAndExpression263);
+            relationalExpression18=relationalExpression();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) adaptor.addChild(root_0, relationalExpression18.getTree());
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:59:24: ( AND relationalExpression )*
+            loop3:
+            do {
+                int alt3=2;
+                int LA3_0 = input.LA(1);
+
+                if ( (LA3_0==AND) ) {
+                    alt3=1;
+                }
+
+
+                switch (alt3) {
+            	case 1 :
+            	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:59:25: AND relationalExpression
+            	    {
+            	    AND19=(Token)input.LT(1);
+            	    match(input,AND,FOLLOW_AND_in_logicalAndExpression266); if (failed) return retval;
+            	    if ( backtracking==0 ) {
+            	    AND19_tree = (Object)adaptor.create(AND19);
+            	    root_0 = (Object)adaptor.becomeRoot(AND19_tree, root_0);
+            	    }
+            	    pushFollow(FOLLOW_relationalExpression_in_logicalAndExpression269);
+            	    relationalExpression20=relationalExpression();
+            	    _fsp--;
+            	    if (failed) return retval;
+            	    if ( backtracking==0 ) adaptor.addChild(root_0, relationalExpression20.getTree());
+
+            	    }
+            	    break;
+
+            	default :
+            	    break loop3;
+                }
+            } while (true);
+
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end logicalAndExpression
+
+    public static class relationalExpression_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start relationalExpression
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:61:1: relationalExpression : sumExpression ( relationalOperator sumExpression )? ;
+    public final relationalExpression_return relationalExpression() throws RecognitionException {
+        relationalExpression_return retval = new relationalExpression_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        sumExpression_return sumExpression21 = null;
+
+        relationalOperator_return relationalOperator22 = null;
+
+        sumExpression_return sumExpression23 = null;
+
+
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:61:22: ( sumExpression ( relationalOperator sumExpression )? )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:61:24: sumExpression ( relationalOperator sumExpression )?
+            {
+            root_0 = (Object)adaptor.nil();
+
+            pushFollow(FOLLOW_sumExpression_in_relationalExpression280);
+            sumExpression21=sumExpression();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) adaptor.addChild(root_0, sumExpression21.getTree());
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:61:38: ( relationalOperator sumExpression )?
+            int alt4=2;
+            int LA4_0 = input.LA(1);
+
+            if ( ((LA4_0>=EQUAL && LA4_0<=MATCHES)) ) {
+                alt4=1;
+            }
+            switch (alt4) {
+                case 1 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:61:39: relationalOperator sumExpression
+                    {
+                    pushFollow(FOLLOW_relationalOperator_in_relationalExpression283);
+                    relationalOperator22=relationalOperator();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) root_0 = (Object)adaptor.becomeRoot(relationalOperator22.getTree(), root_0);
+                    pushFollow(FOLLOW_sumExpression_in_relationalExpression286);
+                    sumExpression23=sumExpression();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, sumExpression23.getTree());
+
+                    }
+                    break;
+
+            }
+
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end relationalExpression
+
+    public static class sumExpression_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start sumExpression
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:63:1: sumExpression : productExpression ( ( PLUS | MINUS ) productExpression )* ;
+    public final sumExpression_return sumExpression() throws RecognitionException {
+        sumExpression_return retval = new sumExpression_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token PLUS25=null;
+        Token MINUS26=null;
+        productExpression_return productExpression24 = null;
+
+        productExpression_return productExpression27 = null;
+
+
+        Object PLUS25_tree=null;
+        Object MINUS26_tree=null;
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:64:2: ( productExpression ( ( PLUS | MINUS ) productExpression )* )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:64:4: productExpression ( ( PLUS | MINUS ) productExpression )*
+            {
+            root_0 = (Object)adaptor.nil();
+
+            pushFollow(FOLLOW_productExpression_in_sumExpression297);
+            productExpression24=productExpression();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) adaptor.addChild(root_0, productExpression24.getTree());
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:64:22: ( ( PLUS | MINUS ) productExpression )*
+            loop6:
+            do {
+                int alt6=2;
+                int LA6_0 = input.LA(1);
+
+                if ( ((LA6_0>=PLUS && LA6_0<=MINUS)) ) {
+                    alt6=1;
+                }
+
+
+                switch (alt6) {
+            	case 1 :
+            	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:64:24: ( PLUS | MINUS ) productExpression
+            	    {
+            	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:64:24: ( PLUS | MINUS )
+            	    int alt5=2;
+            	    int LA5_0 = input.LA(1);
+
+            	    if ( (LA5_0==PLUS) ) {
+            	        alt5=1;
+            	    }
+            	    else if ( (LA5_0==MINUS) ) {
+            	        alt5=2;
+            	    }
+            	    else {
+            	        if (backtracking>0) {failed=true; return retval;}
+            	        NoViableAltException nvae =
+            	            new NoViableAltException("64:24: ( PLUS | MINUS )", 5, 0, input);
+
+            	        throw nvae;
+            	    }
+            	    switch (alt5) {
+            	        case 1 :
+            	            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:64:25: PLUS
+            	            {
+            	            PLUS25=(Token)input.LT(1);
+            	            match(input,PLUS,FOLLOW_PLUS_in_sumExpression302); if (failed) return retval;
+            	            if ( backtracking==0 ) {
+            	            PLUS25_tree = (Object)adaptor.create(PLUS25);
+            	            root_0 = (Object)adaptor.becomeRoot(PLUS25_tree, root_0);
+            	            }
+
+            	            }
+            	            break;
+            	        case 2 :
+            	            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:64:33: MINUS
+            	            {
+            	            MINUS26=(Token)input.LT(1);
+            	            match(input,MINUS,FOLLOW_MINUS_in_sumExpression307); if (failed) return retval;
+            	            if ( backtracking==0 ) {
+            	            MINUS26_tree = (Object)adaptor.create(MINUS26);
+            	            root_0 = (Object)adaptor.becomeRoot(MINUS26_tree, root_0);
+            	            }
+
+            	            }
+            	            break;
+
+            	    }
+
+            	    pushFollow(FOLLOW_productExpression_in_sumExpression311);
+            	    productExpression27=productExpression();
+            	    _fsp--;
+            	    if (failed) return retval;
+            	    if ( backtracking==0 ) adaptor.addChild(root_0, productExpression27.getTree());
+
+            	    }
+            	    break;
+
+            	default :
+            	    break loop6;
+                }
+            } while (true);
+
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end sumExpression
+
+    public static class productExpression_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start productExpression
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:66:1: productExpression : powerExpr ( ( STAR | DIV | MOD ) powerExpr )* ;
+    public final productExpression_return productExpression() throws RecognitionException {
+        productExpression_return retval = new productExpression_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token STAR29=null;
+        Token DIV30=null;
+        Token MOD31=null;
+        powerExpr_return powerExpr28 = null;
+
+        powerExpr_return powerExpr32 = null;
+
+
+        Object STAR29_tree=null;
+        Object DIV30_tree=null;
+        Object MOD31_tree=null;
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:67:2: ( powerExpr ( ( STAR | DIV | MOD ) powerExpr )* )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:67:4: powerExpr ( ( STAR | DIV | MOD ) powerExpr )*
+            {
+            root_0 = (Object)adaptor.nil();
+
+            pushFollow(FOLLOW_powerExpr_in_productExpression322);
+            powerExpr28=powerExpr();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) adaptor.addChild(root_0, powerExpr28.getTree());
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:67:14: ( ( STAR | DIV | MOD ) powerExpr )*
+            loop8:
+            do {
+                int alt8=2;
+                int LA8_0 = input.LA(1);
+
+                if ( ((LA8_0>=STAR && LA8_0<=MOD)) ) {
+                    alt8=1;
+                }
+
+
+                switch (alt8) {
+            	case 1 :
+            	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:67:15: ( STAR | DIV | MOD ) powerExpr
+            	    {
+            	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:67:15: ( STAR | DIV | MOD )
+            	    int alt7=3;
+            	    switch ( input.LA(1) ) {
+            	    case STAR:
+            	        {
+            	        alt7=1;
+            	        }
+            	        break;
+            	    case DIV:
+            	        {
+            	        alt7=2;
+            	        }
+            	        break;
+            	    case MOD:
+            	        {
+            	        alt7=3;
+            	        }
+            	        break;
+            	    default:
+            	        if (backtracking>0) {failed=true; return retval;}
+            	        NoViableAltException nvae =
+            	            new NoViableAltException("67:15: ( STAR | DIV | MOD )", 7, 0, input);
+
+            	        throw nvae;
+            	    }
+
+            	    switch (alt7) {
+            	        case 1 :
+            	            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:67:16: STAR
+            	            {
+            	            STAR29=(Token)input.LT(1);
+            	            match(input,STAR,FOLLOW_STAR_in_productExpression326); if (failed) return retval;
+            	            if ( backtracking==0 ) {
+            	            STAR29_tree = (Object)adaptor.create(STAR29);
+            	            root_0 = (Object)adaptor.becomeRoot(STAR29_tree, root_0);
+            	            }
+
+            	            }
+            	            break;
+            	        case 2 :
+            	            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:67:24: DIV
+            	            {
+            	            DIV30=(Token)input.LT(1);
+            	            match(input,DIV,FOLLOW_DIV_in_productExpression331); if (failed) return retval;
+            	            if ( backtracking==0 ) {
+            	            DIV30_tree = (Object)adaptor.create(DIV30);
+            	            root_0 = (Object)adaptor.becomeRoot(DIV30_tree, root_0);
+            	            }
+
+            	            }
+            	            break;
+            	        case 3 :
+            	            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:67:30: MOD
+            	            {
+            	            MOD31=(Token)input.LT(1);
+            	            match(input,MOD,FOLLOW_MOD_in_productExpression335); if (failed) return retval;
+            	            if ( backtracking==0 ) {
+            	            MOD31_tree = (Object)adaptor.create(MOD31);
+            	            root_0 = (Object)adaptor.becomeRoot(MOD31_tree, root_0);
+            	            }
+
+            	            }
+            	            break;
+
+            	    }
+
+            	    pushFollow(FOLLOW_powerExpr_in_productExpression339);
+            	    powerExpr32=powerExpr();
+            	    _fsp--;
+            	    if (failed) return retval;
+            	    if ( backtracking==0 ) adaptor.addChild(root_0, powerExpr32.getTree());
+
+            	    }
+            	    break;
+
+            	default :
+            	    break loop8;
+                }
+            } while (true);
+
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end productExpression
+
+    public static class powerExpr_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start powerExpr
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:69:1: powerExpr : unaryExpression ( POWER unaryExpression )? ;
+    public final powerExpr_return powerExpr() throws RecognitionException {
+        powerExpr_return retval = new powerExpr_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token POWER34=null;
+        unaryExpression_return unaryExpression33 = null;
+
+        unaryExpression_return unaryExpression35 = null;
+
+
+        Object POWER34_tree=null;
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:69:12: ( unaryExpression ( POWER unaryExpression )? )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:69:14: unaryExpression ( POWER unaryExpression )?
+            {
+            root_0 = (Object)adaptor.nil();
+
+            pushFollow(FOLLOW_unaryExpression_in_powerExpr351);
+            unaryExpression33=unaryExpression();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) adaptor.addChild(root_0, unaryExpression33.getTree());
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:69:30: ( POWER unaryExpression )?
+            int alt9=2;
+            int LA9_0 = input.LA(1);
+
+            if ( (LA9_0==POWER) ) {
+                alt9=1;
+            }
+            switch (alt9) {
+                case 1 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:69:31: POWER unaryExpression
+                    {
+                    POWER34=(Token)input.LT(1);
+                    match(input,POWER,FOLLOW_POWER_in_powerExpr354); if (failed) return retval;
+                    if ( backtracking==0 ) {
+                    POWER34_tree = (Object)adaptor.create(POWER34);
+                    root_0 = (Object)adaptor.becomeRoot(POWER34_tree, root_0);
+                    }
+                    pushFollow(FOLLOW_unaryExpression_in_powerExpr357);
+                    unaryExpression35=unaryExpression();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, unaryExpression35.getTree());
+
+                    }
+                    break;
+
+            }
+
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end powerExpr
+
+    public static class unaryExpression_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start unaryExpression
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:71:1: unaryExpression : ( ( PLUS | MINUS | BANG ) unaryExpression | primaryExpression );
+    public final unaryExpression_return unaryExpression() throws RecognitionException {
+        unaryExpression_return retval = new unaryExpression_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token PLUS36=null;
+        Token MINUS37=null;
+        Token BANG38=null;
+        unaryExpression_return unaryExpression39 = null;
+
+        primaryExpression_return primaryExpression40 = null;
+
+
+        Object PLUS36_tree=null;
+        Object MINUS37_tree=null;
+        Object BANG38_tree=null;
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:72:2: ( ( PLUS | MINUS | BANG ) unaryExpression | primaryExpression )
+            int alt11=2;
+            int LA11_0 = input.LA(1);
+
+            if ( ((LA11_0>=PLUS && LA11_0<=MINUS)||LA11_0==BANG) ) {
+                alt11=1;
+            }
+            else if ( (LA11_0==INTEGER_LITERAL||LA11_0==LPAREN||(LA11_0>=POUND && LA11_0<=ID)||LA11_0==LBRACKET||LA11_0==PROJECT||(LA11_0>=SELECT && LA11_0<=FALSE)||LA11_0==77) ) {
+                alt11=2;
+            }
+            else {
+                if (backtracking>0) {failed=true; return retval;}
+                NoViableAltException nvae =
+                    new NoViableAltException("71:1: unaryExpression : ( ( PLUS | MINUS | BANG ) unaryExpression | primaryExpression );", 11, 0, input);
+
+                throw nvae;
+            }
+            switch (alt11) {
+                case 1 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:72:4: ( PLUS | MINUS | BANG ) unaryExpression
+                    {
+                    root_0 = (Object)adaptor.nil();
+
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:72:4: ( PLUS | MINUS | BANG )
+                    int alt10=3;
+                    switch ( input.LA(1) ) {
+                    case PLUS:
+                        {
+                        alt10=1;
+                        }
+                        break;
+                    case MINUS:
+                        {
+                        alt10=2;
+                        }
+                        break;
+                    case BANG:
+                        {
+                        alt10=3;
+                        }
+                        break;
+                    default:
+                        if (backtracking>0) {failed=true; return retval;}
+                        NoViableAltException nvae =
+                            new NoViableAltException("72:4: ( PLUS | MINUS | BANG )", 10, 0, input);
+
+                        throw nvae;
+                    }
+
+                    switch (alt10) {
+                        case 1 :
+                            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:72:5: PLUS
+                            {
+                            PLUS36=(Token)input.LT(1);
+                            match(input,PLUS,FOLLOW_PLUS_in_unaryExpression371); if (failed) return retval;
+                            if ( backtracking==0 ) {
+                            PLUS36_tree = (Object)adaptor.create(PLUS36);
+                            root_0 = (Object)adaptor.becomeRoot(PLUS36_tree, root_0);
+                            }
+
+                            }
+                            break;
+                        case 2 :
+                            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:72:13: MINUS
+                            {
+                            MINUS37=(Token)input.LT(1);
+                            match(input,MINUS,FOLLOW_MINUS_in_unaryExpression376); if (failed) return retval;
+                            if ( backtracking==0 ) {
+                            MINUS37_tree = (Object)adaptor.create(MINUS37);
+                            root_0 = (Object)adaptor.becomeRoot(MINUS37_tree, root_0);
+                            }
+
+                            }
+                            break;
+                        case 3 :
+                            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:72:22: BANG
+                            {
+                            BANG38=(Token)input.LT(1);
+                            match(input,BANG,FOLLOW_BANG_in_unaryExpression381); if (failed) return retval;
+                            if ( backtracking==0 ) {
+                            BANG38_tree = (Object)adaptor.create(BANG38);
+                            root_0 = (Object)adaptor.becomeRoot(BANG38_tree, root_0);
+                            }
+
+                            }
+                            break;
+
+                    }
+
+                    pushFollow(FOLLOW_unaryExpression_in_unaryExpression385);
+                    unaryExpression39=unaryExpression();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, unaryExpression39.getTree());
+
+                    }
+                    break;
+                case 2 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:73:4: primaryExpression
+                    {
+                    root_0 = (Object)adaptor.nil();
+
+                    pushFollow(FOLLOW_primaryExpression_in_unaryExpression391);
+                    primaryExpression40=primaryExpression();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, primaryExpression40.getTree());
+
+                    }
+                    break;
+
+            }
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end unaryExpression
+
+    public static class primaryExpression_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start primaryExpression
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:75:1: primaryExpression : startNode ( node )? -> ^( EXPRESSION startNode ( node )? ) ;
+    public final primaryExpression_return primaryExpression() throws RecognitionException {
+        primaryExpression_return retval = new primaryExpression_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        startNode_return startNode41 = null;
+
+        node_return node42 = null;
+
+
+        RewriteRuleSubtreeStream stream_node=new RewriteRuleSubtreeStream(adaptor,"rule node");
+        RewriteRuleSubtreeStream stream_startNode=new RewriteRuleSubtreeStream(adaptor,"rule startNode");
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:76:5: ( startNode ( node )? -> ^( EXPRESSION startNode ( node )? ) )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:76:7: startNode ( node )?
+            {
+            pushFollow(FOLLOW_startNode_in_primaryExpression405);
+            startNode41=startNode();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) stream_startNode.add(startNode41.getTree());
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:76:17: ( node )?
+            int alt12=2;
+            int LA12_0 = input.LA(1);
+
+            if ( (LA12_0==DOT||LA12_0==LBRACKET) ) {
+                alt12=1;
+            }
+            switch (alt12) {
+                case 1 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:76:18: node
+                    {
+                    pushFollow(FOLLOW_node_in_primaryExpression408);
+                    node42=node();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) stream_node.add(node42.getTree());
+
+                    }
+                    break;
+
+            }
+
+
+            // AST REWRITE
+            // elements: node, startNode
+            // token labels: 
+            // rule labels: retval
+            // token list labels: 
+            // rule list labels: 
+            if ( backtracking==0 ) {
+            retval.tree = root_0;
+            RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
+
+            root_0 = (Object)adaptor.nil();
+            // 76:25: -> ^( EXPRESSION startNode ( node )? )
+            {
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:76:28: ^( EXPRESSION startNode ( node )? )
+                {
+                Object root_1 = (Object)adaptor.nil();
+                root_1 = (Object)adaptor.becomeRoot(adaptor.create(EXPRESSION, "EXPRESSION"), root_1);
+
+                adaptor.addChild(root_1, stream_startNode.next());
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:76:51: ( node )?
+                if ( stream_node.hasNext() ) {
+                    adaptor.addChild(root_1, stream_node.next());
+
+                }
+                stream_node.reset();
+
+                adaptor.addChild(root_0, root_1);
+                }
+
+            }
+
+            }
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end primaryExpression
+
+    public static class startNode_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start startNode
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:78:1: startNode : ( parenExpr | methodOrProperty | functionOrVar | indexer | literal | type | constructor | projection | selection | firstSelection | lastSelection );
+    public final startNode_return startNode() throws RecognitionException {
+        startNode_return retval = new startNode_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        parenExpr_return parenExpr43 = null;
+
+        methodOrProperty_return methodOrProperty44 = null;
+
+        functionOrVar_return functionOrVar45 = null;
+
+        indexer_return indexer46 = null;
+
+        literal_return literal47 = null;
+
+        type_return type48 = null;
+
+        constructor_return constructor49 = null;
+
+        projection_return projection50 = null;
+
+        selection_return selection51 = null;
+
+        firstSelection_return firstSelection52 = null;
+
+        lastSelection_return lastSelection53 = null;
+
+
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:79:5: ( parenExpr | methodOrProperty | functionOrVar | indexer | literal | type | constructor | projection | selection | firstSelection | lastSelection )
+            int alt13=11;
+            switch ( input.LA(1) ) {
+            case LPAREN:
+                {
+                alt13=1;
+                }
+                break;
+            case ID:
+                {
+                alt13=2;
+                }
+                break;
+            case POUND:
+                {
+                alt13=3;
+                }
+                break;
+            case LBRACKET:
+                {
+                alt13=4;
+                }
+                break;
+            case INTEGER_LITERAL:
+            case STRING_LITERAL:
+            case DQ_STRING_LITERAL:
+            case NULL_LITERAL:
+            case HEXADECIMAL_INTEGER_LITERAL:
+            case REAL_LITERAL:
+            case TRUE:
+            case FALSE:
+                {
+                alt13=5;
+                }
+                break;
+            case TYPE:
+                {
+                alt13=6;
+                }
+                break;
+            case 77:
+                {
+                alt13=7;
+                }
+                break;
+            case PROJECT:
+                {
+                alt13=8;
+                }
+                break;
+            case SELECT:
+                {
+                alt13=9;
+                }
+                break;
+            case SELECT_FIRST:
+                {
+                alt13=10;
+                }
+                break;
+            case SELECT_LAST:
+                {
+                alt13=11;
+                }
+                break;
+            default:
+                if (backtracking>0) {failed=true; return retval;}
+                NoViableAltException nvae =
+                    new NoViableAltException("78:1: startNode : ( parenExpr | methodOrProperty | functionOrVar | indexer | literal | type | constructor | projection | selection | firstSelection | lastSelection );", 13, 0, input);
+
+                throw nvae;
+            }
+
+            switch (alt13) {
+                case 1 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:80:5: parenExpr
+                    {
+                    root_0 = (Object)adaptor.nil();
+
+                    pushFollow(FOLLOW_parenExpr_in_startNode441);
+                    parenExpr43=parenExpr();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, parenExpr43.getTree());
+
+                    }
+                    break;
+                case 2 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:81:7: methodOrProperty
+                    {
+                    root_0 = (Object)adaptor.nil();
+
+                    pushFollow(FOLLOW_methodOrProperty_in_startNode449);
+                    methodOrProperty44=methodOrProperty();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, methodOrProperty44.getTree());
+
+                    }
+                    break;
+                case 3 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:82:7: functionOrVar
+                    {
+                    root_0 = (Object)adaptor.nil();
+
+                    pushFollow(FOLLOW_functionOrVar_in_startNode458);
+                    functionOrVar45=functionOrVar();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, functionOrVar45.getTree());
+
+                    }
+                    break;
+                case 4 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:83:7: indexer
+                    {
+                    root_0 = (Object)adaptor.nil();
+
+                    pushFollow(FOLLOW_indexer_in_startNode466);
+                    indexer46=indexer();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, indexer46.getTree());
+
+                    }
+                    break;
+                case 5 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:84:7: literal
+                    {
+                    root_0 = (Object)adaptor.nil();
+
+                    pushFollow(FOLLOW_literal_in_startNode474);
+                    literal47=literal();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, literal47.getTree());
+
+                    }
+                    break;
+                case 6 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:85:7: type
+                    {
+                    root_0 = (Object)adaptor.nil();
+
+                    pushFollow(FOLLOW_type_in_startNode482);
+                    type48=type();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, type48.getTree());
+
+                    }
+                    break;
+                case 7 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:86:7: constructor
+                    {
+                    root_0 = (Object)adaptor.nil();
+
+                    pushFollow(FOLLOW_constructor_in_startNode490);
+                    constructor49=constructor();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, constructor49.getTree());
+
+                    }
+                    break;
+                case 8 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:87:7: projection
+                    {
+                    root_0 = (Object)adaptor.nil();
+
+                    pushFollow(FOLLOW_projection_in_startNode498);
+                    projection50=projection();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, projection50.getTree());
+
+                    }
+                    break;
+                case 9 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:88:7: selection
+                    {
+                    root_0 = (Object)adaptor.nil();
+
+                    pushFollow(FOLLOW_selection_in_startNode507);
+                    selection51=selection();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, selection51.getTree());
+
+                    }
+                    break;
+                case 10 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:89:7: firstSelection
+                    {
+                    root_0 = (Object)adaptor.nil();
+
+                    pushFollow(FOLLOW_firstSelection_in_startNode516);
+                    firstSelection52=firstSelection();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, firstSelection52.getTree());
+
+                    }
+                    break;
+                case 11 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:90:7: lastSelection
+                    {
+                    root_0 = (Object)adaptor.nil();
+
+                    pushFollow(FOLLOW_lastSelection_in_startNode524);
+                    lastSelection53=lastSelection();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, lastSelection53.getTree());
+
+                    }
+                    break;
+
+            }
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end startNode
+
+    public static class node_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start node
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:93:1: node : ( ( DOT dottedNode ) | nonDottedNode )+ ;
+    public final node_return node() throws RecognitionException {
+        node_return retval = new node_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token DOT54=null;
+        dottedNode_return dottedNode55 = null;
+
+        nonDottedNode_return nonDottedNode56 = null;
+
+
+        Object DOT54_tree=null;
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:94:2: ( ( ( DOT dottedNode ) | nonDottedNode )+ )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:94:4: ( ( DOT dottedNode ) | nonDottedNode )+
+            {
+            root_0 = (Object)adaptor.nil();
+
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:94:4: ( ( DOT dottedNode ) | nonDottedNode )+
+            int cnt14=0;
+            loop14:
+            do {
+                int alt14=3;
+                int LA14_0 = input.LA(1);
+
+                if ( (LA14_0==DOT) ) {
+                    alt14=1;
+                }
+                else if ( (LA14_0==LBRACKET) ) {
+                    alt14=2;
+                }
+
+
+                switch (alt14) {
+            	case 1 :
+            	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:94:5: ( DOT dottedNode )
+            	    {
+            	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:94:5: ( DOT dottedNode )
+            	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:94:6: DOT dottedNode
+            	    {
+            	    DOT54=(Token)input.LT(1);
+            	    match(input,DOT,FOLLOW_DOT_in_node544); if (failed) return retval;
+            	    if ( backtracking==0 ) {
+            	    DOT54_tree = (Object)adaptor.create(DOT54);
+            	    adaptor.addChild(root_0, DOT54_tree);
+            	    }
+            	    pushFollow(FOLLOW_dottedNode_in_node546);
+            	    dottedNode55=dottedNode();
+            	    _fsp--;
+            	    if (failed) return retval;
+            	    if ( backtracking==0 ) adaptor.addChild(root_0, dottedNode55.getTree());
+
+            	    }
+
+
+            	    }
+            	    break;
+            	case 2 :
+            	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:94:24: nonDottedNode
+            	    {
+            	    pushFollow(FOLLOW_nonDottedNode_in_node551);
+            	    nonDottedNode56=nonDottedNode();
+            	    _fsp--;
+            	    if (failed) return retval;
+            	    if ( backtracking==0 ) adaptor.addChild(root_0, nonDottedNode56.getTree());
+
+            	    }
+            	    break;
+
+            	default :
+            	    if ( cnt14 >= 1 ) break loop14;
+            	    if (backtracking>0) {failed=true; return retval;}
+                        EarlyExitException eee =
+                            new EarlyExitException(14, input);
+                        throw eee;
+                }
+                cnt14++;
+            } while (true);
+
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end node
+
+    public static class nonDottedNode_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start nonDottedNode
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:96:1: nonDottedNode : indexer ;
+    public final nonDottedNode_return nonDottedNode() throws RecognitionException {
+        nonDottedNode_return retval = new nonDottedNode_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        indexer_return indexer57 = null;
+
+
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:97:2: ( indexer )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:97:4: indexer
+            {
+            root_0 = (Object)adaptor.nil();
+
+            pushFollow(FOLLOW_indexer_in_nonDottedNode563);
+            indexer57=indexer();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) adaptor.addChild(root_0, indexer57.getTree());
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end nonDottedNode
+
+    public static class dottedNode_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start dottedNode
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:99:1: dottedNode : ( ( methodOrProperty | functionOrVar | projection | selection | firstSelection | lastSelection ) ) ;
+    public final dottedNode_return dottedNode() throws RecognitionException {
+        dottedNode_return retval = new dottedNode_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        methodOrProperty_return methodOrProperty58 = null;
+
+        functionOrVar_return functionOrVar59 = null;
+
+        projection_return projection60 = null;
+
+        selection_return selection61 = null;
+
+        firstSelection_return firstSelection62 = null;
+
+        lastSelection_return lastSelection63 = null;
+
+
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:100:2: ( ( ( methodOrProperty | functionOrVar | projection | selection | firstSelection | lastSelection ) ) )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:101:2: ( ( methodOrProperty | functionOrVar | projection | selection | firstSelection | lastSelection ) )
+            {
+            root_0 = (Object)adaptor.nil();
+
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:101:2: ( ( methodOrProperty | functionOrVar | projection | selection | firstSelection | lastSelection ) )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:101:3: ( methodOrProperty | functionOrVar | projection | selection | firstSelection | lastSelection )
+            {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:101:3: ( methodOrProperty | functionOrVar | projection | selection | firstSelection | lastSelection )
+            int alt15=6;
+            switch ( input.LA(1) ) {
+            case ID:
+                {
+                alt15=1;
+                }
+                break;
+            case POUND:
+                {
+                alt15=2;
+                }
+                break;
+            case PROJECT:
+                {
+                alt15=3;
+                }
+                break;
+            case SELECT:
+                {
+                alt15=4;
+                }
+                break;
+            case SELECT_FIRST:
+                {
+                alt15=5;
+                }
+                break;
+            case SELECT_LAST:
+                {
+                alt15=6;
+                }
+                break;
+            default:
+                if (backtracking>0) {failed=true; return retval;}
+                NoViableAltException nvae =
+                    new NoViableAltException("101:3: ( methodOrProperty | functionOrVar | projection | selection | firstSelection | lastSelection )", 15, 0, input);
+
+                throw nvae;
+            }
+
+            switch (alt15) {
+                case 1 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:101:4: methodOrProperty
+                    {
+                    pushFollow(FOLLOW_methodOrProperty_in_dottedNode576);
+                    methodOrProperty58=methodOrProperty();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, methodOrProperty58.getTree());
+
+                    }
+                    break;
+                case 2 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:102:4: functionOrVar
+                    {
+                    pushFollow(FOLLOW_functionOrVar_in_dottedNode582);
+                    functionOrVar59=functionOrVar();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, functionOrVar59.getTree());
+
+                    }
+                    break;
+                case 3 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:103:7: projection
+                    {
+                    pushFollow(FOLLOW_projection_in_dottedNode590);
+                    projection60=projection();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, projection60.getTree());
+
+                    }
+                    break;
+                case 4 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:104:7: selection
+                    {
+                    pushFollow(FOLLOW_selection_in_dottedNode599);
+                    selection61=selection();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, selection61.getTree());
+
+                    }
+                    break;
+                case 5 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:105:7: firstSelection
+                    {
+                    pushFollow(FOLLOW_firstSelection_in_dottedNode608);
+                    firstSelection62=firstSelection();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, firstSelection62.getTree());
+
+                    }
+                    break;
+                case 6 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:106:7: lastSelection
+                    {
+                    pushFollow(FOLLOW_lastSelection_in_dottedNode617);
+                    lastSelection63=lastSelection();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, lastSelection63.getTree());
+
+                    }
+                    break;
+
+            }
+
+
+            }
+
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end dottedNode
+
+    public static class functionOrVar_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start functionOrVar
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:110:1: functionOrVar : ( ( POUND ID LPAREN )=> function | var );
+    public final functionOrVar_return functionOrVar() throws RecognitionException {
+        functionOrVar_return retval = new functionOrVar_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        function_return function64 = null;
+
+        var_return var65 = null;
+
+
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:111:5: ( ( POUND ID LPAREN )=> function | var )
+            int alt16=2;
+            int LA16_0 = input.LA(1);
+
+            if ( (LA16_0==POUND) ) {
+                int LA16_1 = input.LA(2);
+
+                if ( (LA16_1==ID) ) {
+                    int LA16_2 = input.LA(3);
+
+                    if ( (synpred1()) ) {
+                        alt16=1;
+                    }
+                    else if ( (true) ) {
+                        alt16=2;
+                    }
+                    else {
+                        if (backtracking>0) {failed=true; return retval;}
+                        NoViableAltException nvae =
+                            new NoViableAltException("110:1: functionOrVar : ( ( POUND ID LPAREN )=> function | var );", 16, 2, input);
+
+                        throw nvae;
+                    }
+                }
+                else {
+                    if (backtracking>0) {failed=true; return retval;}
+                    NoViableAltException nvae =
+                        new NoViableAltException("110:1: functionOrVar : ( ( POUND ID LPAREN )=> function | var );", 16, 1, input);
+
+                    throw nvae;
+                }
+            }
+            else {
+                if (backtracking>0) {failed=true; return retval;}
+                NoViableAltException nvae =
+                    new NoViableAltException("110:1: functionOrVar : ( ( POUND ID LPAREN )=> function | var );", 16, 0, input);
+
+                throw nvae;
+            }
+            switch (alt16) {
+                case 1 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:111:7: ( POUND ID LPAREN )=> function
+                    {
+                    root_0 = (Object)adaptor.nil();
+
+                    pushFollow(FOLLOW_function_in_functionOrVar651);
+                    function64=function();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, function64.getTree());
+
+                    }
+                    break;
+                case 2 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:112:7: var
+                    {
+                    root_0 = (Object)adaptor.nil();
+
+                    pushFollow(FOLLOW_var_in_functionOrVar659);
+                    var65=var();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, var65.getTree());
+
+                    }
+                    break;
+
+            }
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end functionOrVar
+
+    public static class function_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start function
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:115:1: function : POUND id= ID methodArgs -> ^( FUNCTIONREF[$id] methodArgs ) ;
+    public final function_return function() throws RecognitionException {
+        function_return retval = new function_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token id=null;
+        Token POUND66=null;
+        methodArgs_return methodArgs67 = null;
+
+
+        Object id_tree=null;
+        Object POUND66_tree=null;
+        RewriteRuleTokenStream stream_POUND=new RewriteRuleTokenStream(adaptor,"token POUND");
+        RewriteRuleTokenStream stream_ID=new RewriteRuleTokenStream(adaptor,"token ID");
+        RewriteRuleSubtreeStream stream_methodArgs=new RewriteRuleSubtreeStream(adaptor,"rule methodArgs");
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:115:10: ( POUND id= ID methodArgs -> ^( FUNCTIONREF[$id] methodArgs ) )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:115:12: POUND id= ID methodArgs
+            {
+            POUND66=(Token)input.LT(1);
+            match(input,POUND,FOLLOW_POUND_in_function676); if (failed) return retval;
+            if ( backtracking==0 ) stream_POUND.add(POUND66);
+
+            id=(Token)input.LT(1);
+            match(input,ID,FOLLOW_ID_in_function680); if (failed) return retval;
+            if ( backtracking==0 ) stream_ID.add(id);
+
+            pushFollow(FOLLOW_methodArgs_in_function682);
+            methodArgs67=methodArgs();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) stream_methodArgs.add(methodArgs67.getTree());
+
+            // AST REWRITE
+            // elements: methodArgs
+            // token labels: 
+            // rule labels: retval
+            // token list labels: 
+            // rule list labels: 
+            if ( backtracking==0 ) {
+            retval.tree = root_0;
+            RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
+
+            root_0 = (Object)adaptor.nil();
+            // 115:35: -> ^( FUNCTIONREF[$id] methodArgs )
+            {
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:115:38: ^( FUNCTIONREF[$id] methodArgs )
+                {
+                Object root_1 = (Object)adaptor.nil();
+                root_1 = (Object)adaptor.becomeRoot(adaptor.create(FUNCTIONREF, id), root_1);
+
+                adaptor.addChild(root_1, stream_methodArgs.next());
+
+                adaptor.addChild(root_0, root_1);
+                }
+
+            }
+
+            }
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end function
+
+    public static class var_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start var
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:117:1: var : POUND id= ID -> ^( VARIABLEREF[$id] ) ;
+    public final var_return var() throws RecognitionException {
+        var_return retval = new var_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token id=null;
+        Token POUND68=null;
+
+        Object id_tree=null;
+        Object POUND68_tree=null;
+        RewriteRuleTokenStream stream_POUND=new RewriteRuleTokenStream(adaptor,"token POUND");
+        RewriteRuleTokenStream stream_ID=new RewriteRuleTokenStream(adaptor,"token ID");
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:117:5: ( POUND id= ID -> ^( VARIABLEREF[$id] ) )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:117:7: POUND id= ID
+            {
+            POUND68=(Token)input.LT(1);
+            match(input,POUND,FOLLOW_POUND_in_var703); if (failed) return retval;
+            if ( backtracking==0 ) stream_POUND.add(POUND68);
+
+            id=(Token)input.LT(1);
+            match(input,ID,FOLLOW_ID_in_var707); if (failed) return retval;
+            if ( backtracking==0 ) stream_ID.add(id);
+
+
+            // AST REWRITE
+            // elements: 
+            // token labels: 
+            // rule labels: retval
+            // token list labels: 
+            // rule list labels: 
+            if ( backtracking==0 ) {
+            retval.tree = root_0;
+            RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
+
+            root_0 = (Object)adaptor.nil();
+            // 117:19: -> ^( VARIABLEREF[$id] )
+            {
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:117:22: ^( VARIABLEREF[$id] )
+                {
+                Object root_1 = (Object)adaptor.nil();
+                root_1 = (Object)adaptor.becomeRoot(adaptor.create(VARIABLEREF, id), root_1);
+
+                adaptor.addChild(root_0, root_1);
+                }
+
+            }
+
+            }
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end var
+
+    public static class methodOrProperty_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start methodOrProperty
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:120:1: methodOrProperty : ( ( ID LPAREN )=>id= ID methodArgs -> ^( METHOD[$id] methodArgs ) | property );
+    public final methodOrProperty_return methodOrProperty() throws RecognitionException {
+        methodOrProperty_return retval = new methodOrProperty_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token id=null;
+        methodArgs_return methodArgs69 = null;
+
+        property_return property70 = null;
+
+
+        Object id_tree=null;
+        RewriteRuleTokenStream stream_ID=new RewriteRuleTokenStream(adaptor,"token ID");
+        RewriteRuleSubtreeStream stream_methodArgs=new RewriteRuleSubtreeStream(adaptor,"rule methodArgs");
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:121:2: ( ( ID LPAREN )=>id= ID methodArgs -> ^( METHOD[$id] methodArgs ) | property )
+            int alt17=2;
+            int LA17_0 = input.LA(1);
+
+            if ( (LA17_0==ID) ) {
+                int LA17_1 = input.LA(2);
+
+                if ( (LA17_1==EOF||(LA17_1>=ASSIGN && LA17_1<=COLON)||(LA17_1>=RPAREN && LA17_1<=POWER)||LA17_1==DOT||(LA17_1>=COMMA && LA17_1<=RBRACKET)||LA17_1==RCURLY||(LA17_1>=EQUAL && LA17_1<=MATCHES)) ) {
+                    alt17=2;
+                }
+                else if ( (LA17_1==LPAREN) && (synpred2())) {
+                    alt17=1;
+                }
+                else {
+                    if (backtracking>0) {failed=true; return retval;}
+                    NoViableAltException nvae =
+                        new NoViableAltException("120:1: methodOrProperty : ( ( ID LPAREN )=>id= ID methodArgs -> ^( METHOD[$id] methodArgs ) | property );", 17, 1, input);
+
+                    throw nvae;
+                }
+            }
+            else {
+                if (backtracking>0) {failed=true; return retval;}
+                NoViableAltException nvae =
+                    new NoViableAltException("120:1: methodOrProperty : ( ( ID LPAREN )=>id= ID methodArgs -> ^( METHOD[$id] methodArgs ) | property );", 17, 0, input);
+
+                throw nvae;
+            }
+            switch (alt17) {
+                case 1 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:121:4: ( ID LPAREN )=>id= ID methodArgs
+                    {
+                    id=(Token)input.LT(1);
+                    match(input,ID,FOLLOW_ID_in_methodOrProperty735); if (failed) return retval;
+                    if ( backtracking==0 ) stream_ID.add(id);
+
+                    pushFollow(FOLLOW_methodArgs_in_methodOrProperty737);
+                    methodArgs69=methodArgs();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) stream_methodArgs.add(methodArgs69.getTree());
+
+                    // AST REWRITE
+                    // elements: methodArgs
+                    // token labels: 
+                    // rule labels: retval
+                    // token list labels: 
+                    // rule list labels: 
+                    if ( backtracking==0 ) {
+                    retval.tree = root_0;
+                    RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
+
+                    root_0 = (Object)adaptor.nil();
+                    // 121:36: -> ^( METHOD[$id] methodArgs )
+                    {
+                        // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:121:39: ^( METHOD[$id] methodArgs )
+                        {
+                        Object root_1 = (Object)adaptor.nil();
+                        root_1 = (Object)adaptor.becomeRoot(adaptor.create(METHOD, id), root_1);
+
+                        adaptor.addChild(root_1, stream_methodArgs.next());
+
+                        adaptor.addChild(root_0, root_1);
+                        }
+
+                    }
+
+                    }
+
+                    }
+                    break;
+                case 2 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:122:4: property
+                    {
+                    root_0 = (Object)adaptor.nil();
+
+                    pushFollow(FOLLOW_property_in_methodOrProperty751);
+                    property70=property();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, property70.getTree());
+
+                    }
+                    break;
+
+            }
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end methodOrProperty
+
+    public static class methodArgs_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start methodArgs
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:128:1: methodArgs : LPAREN ( argument ( COMMA argument )* ( COMMA )? )? RPAREN ;
+    public final methodArgs_return methodArgs() throws RecognitionException {
+        methodArgs_return retval = new methodArgs_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token LPAREN71=null;
+        Token COMMA73=null;
+        Token COMMA75=null;
+        Token RPAREN76=null;
+        argument_return argument72 = null;
+
+        argument_return argument74 = null;
+
+
+        Object LPAREN71_tree=null;
+        Object COMMA73_tree=null;
+        Object COMMA75_tree=null;
+        Object RPAREN76_tree=null;
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:128:12: ( LPAREN ( argument ( COMMA argument )* ( COMMA )? )? RPAREN )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:128:15: LPAREN ( argument ( COMMA argument )* ( COMMA )? )? RPAREN
+            {
+            root_0 = (Object)adaptor.nil();
+
+            LPAREN71=(Token)input.LT(1);
+            match(input,LPAREN,FOLLOW_LPAREN_in_methodArgs766); if (failed) return retval;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:128:23: ( argument ( COMMA argument )* ( COMMA )? )?
+            int alt20=2;
+            int LA20_0 = input.LA(1);
+
+            if ( (LA20_0==INTEGER_LITERAL||LA20_0==LPAREN||(LA20_0>=PLUS && LA20_0<=MINUS)||LA20_0==BANG||(LA20_0>=POUND && LA20_0<=ID)||LA20_0==LBRACKET||LA20_0==PROJECT||(LA20_0>=SELECT && LA20_0<=FALSE)||LA20_0==77) ) {
+                alt20=1;
+            }
+            switch (alt20) {
+                case 1 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:128:24: argument ( COMMA argument )* ( COMMA )?
+                    {
+                    pushFollow(FOLLOW_argument_in_methodArgs770);
+                    argument72=argument();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, argument72.getTree());
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:128:33: ( COMMA argument )*
+                    loop18:
+                    do {
+                        int alt18=2;
+                        int LA18_0 = input.LA(1);
+
+                        if ( (LA18_0==COMMA) ) {
+                            int LA18_1 = input.LA(2);
+
+                            if ( (LA18_1==INTEGER_LITERAL||LA18_1==LPAREN||(LA18_1>=PLUS && LA18_1<=MINUS)||LA18_1==BANG||(LA18_1>=POUND && LA18_1<=ID)||LA18_1==LBRACKET||LA18_1==PROJECT||(LA18_1>=SELECT && LA18_1<=FALSE)||LA18_1==77) ) {
+                                alt18=1;
+                            }
+
+
+                        }
+
+
+                        switch (alt18) {
+                    	case 1 :
+                    	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:128:34: COMMA argument
+                    	    {
+                    	    COMMA73=(Token)input.LT(1);
+                    	    match(input,COMMA,FOLLOW_COMMA_in_methodArgs773); if (failed) return retval;
+                    	    pushFollow(FOLLOW_argument_in_methodArgs776);
+                    	    argument74=argument();
+                    	    _fsp--;
+                    	    if (failed) return retval;
+                    	    if ( backtracking==0 ) adaptor.addChild(root_0, argument74.getTree());
+
+                    	    }
+                    	    break;
+
+                    	default :
+                    	    break loop18;
+                        }
+                    } while (true);
+
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:128:52: ( COMMA )?
+                    int alt19=2;
+                    int LA19_0 = input.LA(1);
+
+                    if ( (LA19_0==COMMA) ) {
+                        alt19=1;
+                    }
+                    switch (alt19) {
+                        case 1 :
+                            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:128:53: COMMA
+                            {
+                            COMMA75=(Token)input.LT(1);
+                            match(input,COMMA,FOLLOW_COMMA_in_methodArgs781); if (failed) return retval;
+
+                            }
+                            break;
+
+                    }
+
+
+                    }
+                    break;
+
+            }
+
+            RPAREN76=(Token)input.LT(1);
+            match(input,RPAREN,FOLLOW_RPAREN_in_methodArgs788); if (failed) return retval;
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end methodArgs
+
+    public static class property_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start property
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:133:1: property : id= ID -> ^( PROPERTY_OR_FIELD[$id] ) ;
+    public final property_return property() throws RecognitionException {
+        property_return retval = new property_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token id=null;
+
+        Object id_tree=null;
+        RewriteRuleTokenStream stream_ID=new RewriteRuleTokenStream(adaptor,"token ID");
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:133:9: (id= ID -> ^( PROPERTY_OR_FIELD[$id] ) )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:133:11: id= ID
+            {
+            id=(Token)input.LT(1);
+            match(input,ID,FOLLOW_ID_in_property801); if (failed) return retval;
+            if ( backtracking==0 ) stream_ID.add(id);
+
+
+            // AST REWRITE
+            // elements: 
+            // token labels: 
+            // rule labels: retval
+            // token list labels: 
+            // rule list labels: 
+            if ( backtracking==0 ) {
+            retval.tree = root_0;
+            RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
+
+            root_0 = (Object)adaptor.nil();
+            // 133:17: -> ^( PROPERTY_OR_FIELD[$id] )
+            {
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:133:20: ^( PROPERTY_OR_FIELD[$id] )
+                {
+                Object root_1 = (Object)adaptor.nil();
+                root_1 = (Object)adaptor.becomeRoot(adaptor.create(PROPERTY_OR_FIELD, id), root_1);
+
+                adaptor.addChild(root_0, root_1);
+                }
+
+            }
+
+            }
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end property
+
+    public static class indexer_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start indexer
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:136:1: indexer : LBRACKET r1= argument ( COMMA r2= argument )* RBRACKET -> ^( INDEXER $r1 ( $r2)* ) ;
+    public final indexer_return indexer() throws RecognitionException {
+        indexer_return retval = new indexer_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token LBRACKET77=null;
+        Token COMMA78=null;
+        Token RBRACKET79=null;
+        argument_return r1 = null;
+
+        argument_return r2 = null;
+
+
+        Object LBRACKET77_tree=null;
+        Object COMMA78_tree=null;
+        Object RBRACKET79_tree=null;
+        RewriteRuleTokenStream stream_COMMA=new RewriteRuleTokenStream(adaptor,"token COMMA");
+        RewriteRuleTokenStream stream_LBRACKET=new RewriteRuleTokenStream(adaptor,"token LBRACKET");
+        RewriteRuleTokenStream stream_RBRACKET=new RewriteRuleTokenStream(adaptor,"token RBRACKET");
+        RewriteRuleSubtreeStream stream_argument=new RewriteRuleSubtreeStream(adaptor,"rule argument");
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:136:8: ( LBRACKET r1= argument ( COMMA r2= argument )* RBRACKET -> ^( INDEXER $r1 ( $r2)* ) )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:136:10: LBRACKET r1= argument ( COMMA r2= argument )* RBRACKET
+            {
+            LBRACKET77=(Token)input.LT(1);
+            match(input,LBRACKET,FOLLOW_LBRACKET_in_indexer816); if (failed) return retval;
+            if ( backtracking==0 ) stream_LBRACKET.add(LBRACKET77);
+
+            pushFollow(FOLLOW_argument_in_indexer820);
+            r1=argument();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) stream_argument.add(r1.getTree());
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:136:31: ( COMMA r2= argument )*
+            loop21:
+            do {
+                int alt21=2;
+                int LA21_0 = input.LA(1);
+
+                if ( (LA21_0==COMMA) ) {
+                    alt21=1;
+                }
+
+
+                switch (alt21) {
+            	case 1 :
+            	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:136:32: COMMA r2= argument
+            	    {
+            	    COMMA78=(Token)input.LT(1);
+            	    match(input,COMMA,FOLLOW_COMMA_in_indexer823); if (failed) return retval;
+            	    if ( backtracking==0 ) stream_COMMA.add(COMMA78);
+
+            	    pushFollow(FOLLOW_argument_in_indexer827);
+            	    r2=argument();
+            	    _fsp--;
+            	    if (failed) return retval;
+            	    if ( backtracking==0 ) stream_argument.add(r2.getTree());
+
+            	    }
+            	    break;
+
+            	default :
+            	    break loop21;
+                }
+            } while (true);
+
+            RBRACKET79=(Token)input.LT(1);
+            match(input,RBRACKET,FOLLOW_RBRACKET_in_indexer831); if (failed) return retval;
+            if ( backtracking==0 ) stream_RBRACKET.add(RBRACKET79);
+
+
+            // AST REWRITE
+            // elements: r2, r1
+            // token labels: 
+            // rule labels: r2, retval, r1
+            // token list labels: 
+            // rule list labels: 
+            if ( backtracking==0 ) {
+            retval.tree = root_0;
+            RewriteRuleSubtreeStream stream_r2=new RewriteRuleSubtreeStream(adaptor,"token r2",r2!=null?r2.tree:null);
+            RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
+            RewriteRuleSubtreeStream stream_r1=new RewriteRuleSubtreeStream(adaptor,"token r1",r1!=null?r1.tree:null);
+
+            root_0 = (Object)adaptor.nil();
+            // 136:61: -> ^( INDEXER $r1 ( $r2)* )
+            {
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:136:64: ^( INDEXER $r1 ( $r2)* )
+                {
+                Object root_1 = (Object)adaptor.nil();
+                root_1 = (Object)adaptor.becomeRoot(adaptor.create(INDEXER, "INDEXER"), root_1);
+
+                adaptor.addChild(root_1, stream_r1.next());
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:136:78: ( $r2)*
+                while ( stream_r2.hasNext() ) {
+                    adaptor.addChild(root_1, stream_r2.next());
+
+                }
+                stream_r2.reset();
+
+                adaptor.addChild(root_0, root_1);
+                }
+
+            }
+
+            }
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end indexer
+
+    public static class projection_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start projection
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:140:1: projection : PROJECT expression RCURLY ;
+    public final projection_return projection() throws RecognitionException {
+        projection_return retval = new projection_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token PROJECT80=null;
+        Token RCURLY82=null;
+        expression_return expression81 = null;
+
+
+        Object PROJECT80_tree=null;
+        Object RCURLY82_tree=null;
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:140:11: ( PROJECT expression RCURLY )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:140:13: PROJECT expression RCURLY
+            {
+            root_0 = (Object)adaptor.nil();
+
+            PROJECT80=(Token)input.LT(1);
+            match(input,PROJECT,FOLLOW_PROJECT_in_projection857); if (failed) return retval;
+            if ( backtracking==0 ) {
+            PROJECT80_tree = (Object)adaptor.create(PROJECT80);
+            root_0 = (Object)adaptor.becomeRoot(PROJECT80_tree, root_0);
+            }
+            pushFollow(FOLLOW_expression_in_projection860);
+            expression81=expression();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) adaptor.addChild(root_0, expression81.getTree());
+            RCURLY82=(Token)input.LT(1);
+            match(input,RCURLY,FOLLOW_RCURLY_in_projection862); if (failed) return retval;
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end projection
+
+    public static class selection_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start selection
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:142:1: selection : SELECT expression RCURLY ;
+    public final selection_return selection() throws RecognitionException {
+        selection_return retval = new selection_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token SELECT83=null;
+        Token RCURLY85=null;
+        expression_return expression84 = null;
+
+
+        Object SELECT83_tree=null;
+        Object RCURLY85_tree=null;
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:142:10: ( SELECT expression RCURLY )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:142:12: SELECT expression RCURLY
+            {
+            root_0 = (Object)adaptor.nil();
+
+            SELECT83=(Token)input.LT(1);
+            match(input,SELECT,FOLLOW_SELECT_in_selection870); if (failed) return retval;
+            if ( backtracking==0 ) {
+            SELECT83_tree = (Object)adaptor.create(SELECT83);
+            root_0 = (Object)adaptor.becomeRoot(SELECT83_tree, root_0);
+            }
+            pushFollow(FOLLOW_expression_in_selection873);
+            expression84=expression();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) adaptor.addChild(root_0, expression84.getTree());
+            RCURLY85=(Token)input.LT(1);
+            match(input,RCURLY,FOLLOW_RCURLY_in_selection875); if (failed) return retval;
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end selection
+
+    public static class firstSelection_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start firstSelection
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:144:1: firstSelection : SELECT_FIRST expression RCURLY ;
+    public final firstSelection_return firstSelection() throws RecognitionException {
+        firstSelection_return retval = new firstSelection_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token SELECT_FIRST86=null;
+        Token RCURLY88=null;
+        expression_return expression87 = null;
+
+
+        Object SELECT_FIRST86_tree=null;
+        Object RCURLY88_tree=null;
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:144:15: ( SELECT_FIRST expression RCURLY )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:144:17: SELECT_FIRST expression RCURLY
+            {
+            root_0 = (Object)adaptor.nil();
+
+            SELECT_FIRST86=(Token)input.LT(1);
+            match(input,SELECT_FIRST,FOLLOW_SELECT_FIRST_in_firstSelection883); if (failed) return retval;
+            if ( backtracking==0 ) {
+            SELECT_FIRST86_tree = (Object)adaptor.create(SELECT_FIRST86);
+            root_0 = (Object)adaptor.becomeRoot(SELECT_FIRST86_tree, root_0);
+            }
+            pushFollow(FOLLOW_expression_in_firstSelection886);
+            expression87=expression();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) adaptor.addChild(root_0, expression87.getTree());
+            RCURLY88=(Token)input.LT(1);
+            match(input,RCURLY,FOLLOW_RCURLY_in_firstSelection888); if (failed) return retval;
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end firstSelection
+
+    public static class lastSelection_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start lastSelection
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:146:1: lastSelection : SELECT_LAST expression RCURLY ;
+    public final lastSelection_return lastSelection() throws RecognitionException {
+        lastSelection_return retval = new lastSelection_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token SELECT_LAST89=null;
+        Token RCURLY91=null;
+        expression_return expression90 = null;
+
+
+        Object SELECT_LAST89_tree=null;
+        Object RCURLY91_tree=null;
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:146:14: ( SELECT_LAST expression RCURLY )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:146:16: SELECT_LAST expression RCURLY
+            {
+            root_0 = (Object)adaptor.nil();
+
+            SELECT_LAST89=(Token)input.LT(1);
+            match(input,SELECT_LAST,FOLLOW_SELECT_LAST_in_lastSelection896); if (failed) return retval;
+            if ( backtracking==0 ) {
+            SELECT_LAST89_tree = (Object)adaptor.create(SELECT_LAST89);
+            root_0 = (Object)adaptor.becomeRoot(SELECT_LAST89_tree, root_0);
+            }
+            pushFollow(FOLLOW_expression_in_lastSelection899);
+            expression90=expression();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) adaptor.addChild(root_0, expression90.getTree());
+            RCURLY91=(Token)input.LT(1);
+            match(input,RCURLY,FOLLOW_RCURLY_in_lastSelection901); if (failed) return retval;
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end lastSelection
+
+    public static class type_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start type
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:149:1: type : TYPE qualifiedId RPAREN -> ^( TYPEREF qualifiedId ) ;
+    public final type_return type() throws RecognitionException {
+        type_return retval = new type_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token TYPE92=null;
+        Token RPAREN94=null;
+        qualifiedId_return qualifiedId93 = null;
+
+
+        Object TYPE92_tree=null;
+        Object RPAREN94_tree=null;
+        RewriteRuleTokenStream stream_RPAREN=new RewriteRuleTokenStream(adaptor,"token RPAREN");
+        RewriteRuleTokenStream stream_TYPE=new RewriteRuleTokenStream(adaptor,"token TYPE");
+        RewriteRuleSubtreeStream stream_qualifiedId=new RewriteRuleSubtreeStream(adaptor,"rule qualifiedId");
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:149:5: ( TYPE qualifiedId RPAREN -> ^( TYPEREF qualifiedId ) )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:149:7: TYPE qualifiedId RPAREN
+            {
+            TYPE92=(Token)input.LT(1);
+            match(input,TYPE,FOLLOW_TYPE_in_type910); if (failed) return retval;
+            if ( backtracking==0 ) stream_TYPE.add(TYPE92);
+
+            pushFollow(FOLLOW_qualifiedId_in_type912);
+            qualifiedId93=qualifiedId();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) stream_qualifiedId.add(qualifiedId93.getTree());
+            RPAREN94=(Token)input.LT(1);
+            match(input,RPAREN,FOLLOW_RPAREN_in_type914); if (failed) return retval;
+            if ( backtracking==0 ) stream_RPAREN.add(RPAREN94);
+
+
+            // AST REWRITE
+            // elements: qualifiedId
+            // token labels: 
+            // rule labels: retval
+            // token list labels: 
+            // rule list labels: 
+            if ( backtracking==0 ) {
+            retval.tree = root_0;
+            RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
+
+            root_0 = (Object)adaptor.nil();
+            // 149:31: -> ^( TYPEREF qualifiedId )
+            {
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:149:34: ^( TYPEREF qualifiedId )
+                {
+                Object root_1 = (Object)adaptor.nil();
+                root_1 = (Object)adaptor.becomeRoot(adaptor.create(TYPEREF, "TYPEREF"), root_1);
+
+                adaptor.addChild(root_1, stream_qualifiedId.next());
+
+                adaptor.addChild(root_0, root_1);
+                }
+
+            }
+
+            }
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end type
+
+    public static class constructor_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start constructor
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:153:1: constructor : ( 'new' qualifiedId LPAREN )=> 'new' qualifiedId ctorArgs -> ^( CONSTRUCTOR qualifiedId ctorArgs ) ;
+    public final constructor_return constructor() throws RecognitionException {
+        constructor_return retval = new constructor_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token string_literal95=null;
+        qualifiedId_return qualifiedId96 = null;
+
+        ctorArgs_return ctorArgs97 = null;
+
+
+        Object string_literal95_tree=null;
+        RewriteRuleTokenStream stream_77=new RewriteRuleTokenStream(adaptor,"token 77");
+        RewriteRuleSubtreeStream stream_qualifiedId=new RewriteRuleSubtreeStream(adaptor,"rule qualifiedId");
+        RewriteRuleSubtreeStream stream_ctorArgs=new RewriteRuleSubtreeStream(adaptor,"rule ctorArgs");
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:154:2: ( ( 'new' qualifiedId LPAREN )=> 'new' qualifiedId ctorArgs -> ^( CONSTRUCTOR qualifiedId ctorArgs ) )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:154:4: ( 'new' qualifiedId LPAREN )=> 'new' qualifiedId ctorArgs
+            {
+            string_literal95=(Token)input.LT(1);
+            match(input,77,FOLLOW_77_in_constructor945); if (failed) return retval;
+            if ( backtracking==0 ) stream_77.add(string_literal95);
+
+            pushFollow(FOLLOW_qualifiedId_in_constructor947);
+            qualifiedId96=qualifiedId();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) stream_qualifiedId.add(qualifiedId96.getTree());
+            pushFollow(FOLLOW_ctorArgs_in_constructor949);
+            ctorArgs97=ctorArgs();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) stream_ctorArgs.add(ctorArgs97.getTree());
+
+            // AST REWRITE
+            // elements: ctorArgs, qualifiedId
+            // token labels: 
+            // rule labels: retval
+            // token list labels: 
+            // rule list labels: 
+            if ( backtracking==0 ) {
+            retval.tree = root_0;
+            RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
+
+            root_0 = (Object)adaptor.nil();
+            // 154:61: -> ^( CONSTRUCTOR qualifiedId ctorArgs )
+            {
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:154:64: ^( CONSTRUCTOR qualifiedId ctorArgs )
+                {
+                Object root_1 = (Object)adaptor.nil();
+                root_1 = (Object)adaptor.becomeRoot(adaptor.create(CONSTRUCTOR, "CONSTRUCTOR"), root_1);
+
+                adaptor.addChild(root_1, stream_qualifiedId.next());
+                adaptor.addChild(root_1, stream_ctorArgs.next());
+
+                adaptor.addChild(root_0, root_1);
+                }
+
+            }
+
+            }
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end constructor
+
+    public static class ctorArgs_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start ctorArgs
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:157:1: ctorArgs : LPAREN ( namedArgument ( COMMA namedArgument )* )? RPAREN ;
+    public final ctorArgs_return ctorArgs() throws RecognitionException {
+        ctorArgs_return retval = new ctorArgs_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token LPAREN98=null;
+        Token COMMA100=null;
+        Token RPAREN102=null;
+        namedArgument_return namedArgument99 = null;
+
+        namedArgument_return namedArgument101 = null;
+
+
+        Object LPAREN98_tree=null;
+        Object COMMA100_tree=null;
+        Object RPAREN102_tree=null;
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:158:2: ( LPAREN ( namedArgument ( COMMA namedArgument )* )? RPAREN )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:158:4: LPAREN ( namedArgument ( COMMA namedArgument )* )? RPAREN
+            {
+            root_0 = (Object)adaptor.nil();
+
+            LPAREN98=(Token)input.LT(1);
+            match(input,LPAREN,FOLLOW_LPAREN_in_ctorArgs971); if (failed) return retval;
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:158:12: ( namedArgument ( COMMA namedArgument )* )?
+            int alt23=2;
+            int LA23_0 = input.LA(1);
+
+            if ( (LA23_0==INTEGER_LITERAL||LA23_0==LPAREN||(LA23_0>=PLUS && LA23_0<=MINUS)||LA23_0==BANG||(LA23_0>=POUND && LA23_0<=ID)||LA23_0==LBRACKET||LA23_0==PROJECT||(LA23_0>=SELECT && LA23_0<=FALSE)||LA23_0==77) ) {
+                alt23=1;
+            }
+            switch (alt23) {
+                case 1 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:158:13: namedArgument ( COMMA namedArgument )*
+                    {
+                    pushFollow(FOLLOW_namedArgument_in_ctorArgs975);
+                    namedArgument99=namedArgument();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, namedArgument99.getTree());
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:158:27: ( COMMA namedArgument )*
+                    loop22:
+                    do {
+                        int alt22=2;
+                        int LA22_0 = input.LA(1);
+
+                        if ( (LA22_0==COMMA) ) {
+                            alt22=1;
+                        }
+
+
+                        switch (alt22) {
+                    	case 1 :
+                    	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:158:28: COMMA namedArgument
+                    	    {
+                    	    COMMA100=(Token)input.LT(1);
+                    	    match(input,COMMA,FOLLOW_COMMA_in_ctorArgs978); if (failed) return retval;
+                    	    pushFollow(FOLLOW_namedArgument_in_ctorArgs981);
+                    	    namedArgument101=namedArgument();
+                    	    _fsp--;
+                    	    if (failed) return retval;
+                    	    if ( backtracking==0 ) adaptor.addChild(root_0, namedArgument101.getTree());
+
+                    	    }
+                    	    break;
+
+                    	default :
+                    	    break loop22;
+                        }
+                    } while (true);
+
+
+                    }
+                    break;
+
+            }
+
+            RPAREN102=(Token)input.LT(1);
+            match(input,RPAREN,FOLLOW_RPAREN_in_ctorArgs987); if (failed) return retval;
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end ctorArgs
+
+    public static class argument_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start argument
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:160:1: argument : expression ;
+    public final argument_return argument() throws RecognitionException {
+        argument_return retval = new argument_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        expression_return expression103 = null;
+
+
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:160:10: ( expression )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:160:12: expression
+            {
+            root_0 = (Object)adaptor.nil();
+
+            pushFollow(FOLLOW_expression_in_argument996);
+            expression103=expression();
+            _fsp--;
+            if (failed) return retval;
+            if ( backtracking==0 ) adaptor.addChild(root_0, expression103.getTree());
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end argument
+
+    public static class namedArgument_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start namedArgument
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:162:1: namedArgument : ( ( ID ASSIGN )=>id= ID ASSIGN expression -> ^( NAMED_ARGUMENT[$id] expression ) | argument );
+    public final namedArgument_return namedArgument() throws RecognitionException {
+        namedArgument_return retval = new namedArgument_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token id=null;
+        Token ASSIGN104=null;
+        expression_return expression105 = null;
+
+        argument_return argument106 = null;
+
+
+        Object id_tree=null;
+        Object ASSIGN104_tree=null;
+        RewriteRuleTokenStream stream_ASSIGN=new RewriteRuleTokenStream(adaptor,"token ASSIGN");
+        RewriteRuleTokenStream stream_ID=new RewriteRuleTokenStream(adaptor,"token ID");
+        RewriteRuleSubtreeStream stream_expression=new RewriteRuleSubtreeStream(adaptor,"rule expression");
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:163:5: ( ( ID ASSIGN )=>id= ID ASSIGN expression -> ^( NAMED_ARGUMENT[$id] expression ) | argument )
+            int alt24=2;
+            int LA24_0 = input.LA(1);
+
+            if ( (LA24_0==ID) ) {
+                int LA24_1 = input.LA(2);
+
+                if ( (LA24_1==ASSIGN) ) {
+                    int LA24_21 = input.LA(3);
+
+                    if ( (synpred4()) ) {
+                        alt24=1;
+                    }
+                    else if ( (true) ) {
+                        alt24=2;
+                    }
+                    else {
+                        if (backtracking>0) {failed=true; return retval;}
+                        NoViableAltException nvae =
+                            new NoViableAltException("162:1: namedArgument : ( ( ID ASSIGN )=>id= ID ASSIGN expression -> ^( NAMED_ARGUMENT[$id] expression ) | argument );", 24, 21, input);
+
+                        throw nvae;
+                    }
+                }
+                else if ( ((LA24_1>=DEFAULT && LA24_1<=QMARK)||(LA24_1>=LPAREN && LA24_1<=POWER)||LA24_1==DOT||(LA24_1>=COMMA && LA24_1<=LBRACKET)||(LA24_1>=EQUAL && LA24_1<=MATCHES)) ) {
+                    alt24=2;
+                }
+                else {
+                    if (backtracking>0) {failed=true; return retval;}
+                    NoViableAltException nvae =
+                        new NoViableAltException("162:1: namedArgument : ( ( ID ASSIGN )=>id= ID ASSIGN expression -> ^( NAMED_ARGUMENT[$id] expression ) | argument );", 24, 1, input);
+
+                    throw nvae;
+                }
+            }
+            else if ( (LA24_0==INTEGER_LITERAL||LA24_0==LPAREN||(LA24_0>=PLUS && LA24_0<=MINUS)||LA24_0==BANG||LA24_0==POUND||LA24_0==LBRACKET||LA24_0==PROJECT||(LA24_0>=SELECT && LA24_0<=FALSE)||LA24_0==77) ) {
+                alt24=2;
+            }
+            else {
+                if (backtracking>0) {failed=true; return retval;}
+                NoViableAltException nvae =
+                    new NoViableAltException("162:1: namedArgument : ( ( ID ASSIGN )=>id= ID ASSIGN expression -> ^( NAMED_ARGUMENT[$id] expression ) | argument );", 24, 0, input);
+
+                throw nvae;
+            }
+            switch (alt24) {
+                case 1 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:163:7: ( ID ASSIGN )=>id= ID ASSIGN expression
+                    {
+                    id=(Token)input.LT(1);
+                    match(input,ID,FOLLOW_ID_in_namedArgument1019); if (failed) return retval;
+                    if ( backtracking==0 ) stream_ID.add(id);
+
+                    ASSIGN104=(Token)input.LT(1);
+                    match(input,ASSIGN,FOLLOW_ASSIGN_in_namedArgument1021); if (failed) return retval;
+                    if ( backtracking==0 ) stream_ASSIGN.add(ASSIGN104);
+
+                    pushFollow(FOLLOW_expression_in_namedArgument1023);
+                    expression105=expression();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) stream_expression.add(expression105.getTree());
+
+                    // AST REWRITE
+                    // elements: expression
+                    // token labels: 
+                    // rule labels: retval
+                    // token list labels: 
+                    // rule list labels: 
+                    if ( backtracking==0 ) {
+                    retval.tree = root_0;
+                    RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
+
+                    root_0 = (Object)adaptor.nil();
+                    // 164:19: -> ^( NAMED_ARGUMENT[$id] expression )
+                    {
+                        // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:164:22: ^( NAMED_ARGUMENT[$id] expression )
+                        {
+                        Object root_1 = (Object)adaptor.nil();
+                        root_1 = (Object)adaptor.becomeRoot(adaptor.create(NAMED_ARGUMENT, id), root_1);
+
+                        adaptor.addChild(root_1, stream_expression.next());
+
+                        adaptor.addChild(root_0, root_1);
+                        }
+
+                    }
+
+                    }
+
+                    }
+                    break;
+                case 2 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:165:7: argument
+                    {
+                    root_0 = (Object)adaptor.nil();
+
+                    pushFollow(FOLLOW_argument_in_namedArgument1059);
+                    argument106=argument();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, argument106.getTree());
+
+                    }
+                    break;
+
+            }
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end namedArgument
+
+    public static class qualifiedId_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start qualifiedId
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:167:1: qualifiedId : ID ( DOT ID )* -> ^( QUALIFIED_IDENTIFIER ( ID )* ) ;
+    public final qualifiedId_return qualifiedId() throws RecognitionException {
+        qualifiedId_return retval = new qualifiedId_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token ID107=null;
+        Token DOT108=null;
+        Token ID109=null;
+
+        Object ID107_tree=null;
+        Object DOT108_tree=null;
+        Object ID109_tree=null;
+        RewriteRuleTokenStream stream_ID=new RewriteRuleTokenStream(adaptor,"token ID");
+        RewriteRuleTokenStream stream_DOT=new RewriteRuleTokenStream(adaptor,"token DOT");
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:167:13: ( ID ( DOT ID )* -> ^( QUALIFIED_IDENTIFIER ( ID )* ) )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:167:15: ID ( DOT ID )*
+            {
+            ID107=(Token)input.LT(1);
+            match(input,ID,FOLLOW_ID_in_qualifiedId1071); if (failed) return retval;
+            if ( backtracking==0 ) stream_ID.add(ID107);
+
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:167:18: ( DOT ID )*
+            loop25:
+            do {
+                int alt25=2;
+                int LA25_0 = input.LA(1);
+
+                if ( (LA25_0==DOT) ) {
+                    alt25=1;
+                }
+
+
+                switch (alt25) {
+            	case 1 :
+            	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:167:19: DOT ID
+            	    {
+            	    DOT108=(Token)input.LT(1);
+            	    match(input,DOT,FOLLOW_DOT_in_qualifiedId1074); if (failed) return retval;
+            	    if ( backtracking==0 ) stream_DOT.add(DOT108);
+
+            	    ID109=(Token)input.LT(1);
+            	    match(input,ID,FOLLOW_ID_in_qualifiedId1076); if (failed) return retval;
+            	    if ( backtracking==0 ) stream_ID.add(ID109);
+
+
+            	    }
+            	    break;
+
+            	default :
+            	    break loop25;
+                }
+            } while (true);
+
+
+            // AST REWRITE
+            // elements: ID
+            // token labels: 
+            // rule labels: retval
+            // token list labels: 
+            // rule list labels: 
+            if ( backtracking==0 ) {
+            retval.tree = root_0;
+            RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
+
+            root_0 = (Object)adaptor.nil();
+            // 167:28: -> ^( QUALIFIED_IDENTIFIER ( ID )* )
+            {
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:167:31: ^( QUALIFIED_IDENTIFIER ( ID )* )
+                {
+                Object root_1 = (Object)adaptor.nil();
+                root_1 = (Object)adaptor.becomeRoot(adaptor.create(QUALIFIED_IDENTIFIER, "QUALIFIED_IDENTIFIER"), root_1);
+
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:167:54: ( ID )*
+                while ( stream_ID.hasNext() ) {
+                    adaptor.addChild(root_1, stream_ID.next());
+
+                }
+                stream_ID.reset();
+
+                adaptor.addChild(root_0, root_1);
+                }
+
+            }
+
+            }
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end qualifiedId
+
+    public static class contextName_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start contextName
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:169:1: contextName : ID ( DIV ID )* -> ^( QUALIFIED_IDENTIFIER ( ID )* ) ;
+    public final contextName_return contextName() throws RecognitionException {
+        contextName_return retval = new contextName_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token ID110=null;
+        Token DIV111=null;
+        Token ID112=null;
+
+        Object ID110_tree=null;
+        Object DIV111_tree=null;
+        Object ID112_tree=null;
+        RewriteRuleTokenStream stream_DIV=new RewriteRuleTokenStream(adaptor,"token DIV");
+        RewriteRuleTokenStream stream_ID=new RewriteRuleTokenStream(adaptor,"token ID");
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:169:13: ( ID ( DIV ID )* -> ^( QUALIFIED_IDENTIFIER ( ID )* ) )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:169:15: ID ( DIV ID )*
+            {
+            ID110=(Token)input.LT(1);
+            match(input,ID,FOLLOW_ID_in_contextName1095); if (failed) return retval;
+            if ( backtracking==0 ) stream_ID.add(ID110);
+
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:169:18: ( DIV ID )*
+            loop26:
+            do {
+                int alt26=2;
+                int LA26_0 = input.LA(1);
+
+                if ( (LA26_0==DIV) ) {
+                    alt26=1;
+                }
+
+
+                switch (alt26) {
+            	case 1 :
+            	    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:169:19: DIV ID
+            	    {
+            	    DIV111=(Token)input.LT(1);
+            	    match(input,DIV,FOLLOW_DIV_in_contextName1098); if (failed) return retval;
+            	    if ( backtracking==0 ) stream_DIV.add(DIV111);
+
+            	    ID112=(Token)input.LT(1);
+            	    match(input,ID,FOLLOW_ID_in_contextName1100); if (failed) return retval;
+            	    if ( backtracking==0 ) stream_ID.add(ID112);
+
+
+            	    }
+            	    break;
+
+            	default :
+            	    break loop26;
+                }
+            } while (true);
+
+
+            // AST REWRITE
+            // elements: ID
+            // token labels: 
+            // rule labels: retval
+            // token list labels: 
+            // rule list labels: 
+            if ( backtracking==0 ) {
+            retval.tree = root_0;
+            RewriteRuleSubtreeStream stream_retval=new RewriteRuleSubtreeStream(adaptor,"token retval",retval!=null?retval.tree:null);
+
+            root_0 = (Object)adaptor.nil();
+            // 169:28: -> ^( QUALIFIED_IDENTIFIER ( ID )* )
+            {
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:169:31: ^( QUALIFIED_IDENTIFIER ( ID )* )
+                {
+                Object root_1 = (Object)adaptor.nil();
+                root_1 = (Object)adaptor.becomeRoot(adaptor.create(QUALIFIED_IDENTIFIER, "QUALIFIED_IDENTIFIER"), root_1);
+
+                // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:169:54: ( ID )*
+                while ( stream_ID.hasNext() ) {
+                    adaptor.addChild(root_1, stream_ID.next());
+
+                }
+                stream_ID.reset();
+
+                adaptor.addChild(root_0, root_1);
+                }
+
+            }
+
+            }
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end contextName
+
+    public static class literal_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start literal
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:171:1: literal : ( INTEGER_LITERAL | STRING_LITERAL | DQ_STRING_LITERAL | boolLiteral | NULL_LITERAL | HEXADECIMAL_INTEGER_LITERAL | REAL_LITERAL );
+    public final literal_return literal() throws RecognitionException {
+        literal_return retval = new literal_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token INTEGER_LITERAL113=null;
+        Token STRING_LITERAL114=null;
+        Token DQ_STRING_LITERAL115=null;
+        Token NULL_LITERAL117=null;
+        Token HEXADECIMAL_INTEGER_LITERAL118=null;
+        Token REAL_LITERAL119=null;
+        boolLiteral_return boolLiteral116 = null;
+
+
+        Object INTEGER_LITERAL113_tree=null;
+        Object STRING_LITERAL114_tree=null;
+        Object DQ_STRING_LITERAL115_tree=null;
+        Object NULL_LITERAL117_tree=null;
+        Object HEXADECIMAL_INTEGER_LITERAL118_tree=null;
+        Object REAL_LITERAL119_tree=null;
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:172:2: ( INTEGER_LITERAL | STRING_LITERAL | DQ_STRING_LITERAL | boolLiteral | NULL_LITERAL | HEXADECIMAL_INTEGER_LITERAL | REAL_LITERAL )
+            int alt27=7;
+            switch ( input.LA(1) ) {
+            case INTEGER_LITERAL:
+                {
+                alt27=1;
+                }
+                break;
+            case STRING_LITERAL:
+                {
+                alt27=2;
+                }
+                break;
+            case DQ_STRING_LITERAL:
+                {
+                alt27=3;
+                }
+                break;
+            case TRUE:
+            case FALSE:
+                {
+                alt27=4;
+                }
+                break;
+            case NULL_LITERAL:
+                {
+                alt27=5;
+                }
+                break;
+            case HEXADECIMAL_INTEGER_LITERAL:
+                {
+                alt27=6;
+                }
+                break;
+            case REAL_LITERAL:
+                {
+                alt27=7;
+                }
+                break;
+            default:
+                if (backtracking>0) {failed=true; return retval;}
+                NoViableAltException nvae =
+                    new NoViableAltException("171:1: literal : ( INTEGER_LITERAL | STRING_LITERAL | DQ_STRING_LITERAL | boolLiteral | NULL_LITERAL | HEXADECIMAL_INTEGER_LITERAL | REAL_LITERAL );", 27, 0, input);
+
+                throw nvae;
+            }
+
+            switch (alt27) {
+                case 1 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:172:4: INTEGER_LITERAL
+                    {
+                    root_0 = (Object)adaptor.nil();
+
+                    INTEGER_LITERAL113=(Token)input.LT(1);
+                    match(input,INTEGER_LITERAL,FOLLOW_INTEGER_LITERAL_in_literal1121); if (failed) return retval;
+                    if ( backtracking==0 ) {
+                    INTEGER_LITERAL113_tree = (Object)adaptor.create(INTEGER_LITERAL113);
+                    adaptor.addChild(root_0, INTEGER_LITERAL113_tree);
+                    }
+
+                    }
+                    break;
+                case 2 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:173:4: STRING_LITERAL
+                    {
+                    root_0 = (Object)adaptor.nil();
+
+                    STRING_LITERAL114=(Token)input.LT(1);
+                    match(input,STRING_LITERAL,FOLLOW_STRING_LITERAL_in_literal1127); if (failed) return retval;
+                    if ( backtracking==0 ) {
+                    STRING_LITERAL114_tree = (Object)adaptor.create(STRING_LITERAL114);
+                    adaptor.addChild(root_0, STRING_LITERAL114_tree);
+                    }
+
+                    }
+                    break;
+                case 3 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:174:4: DQ_STRING_LITERAL
+                    {
+                    root_0 = (Object)adaptor.nil();
+
+                    DQ_STRING_LITERAL115=(Token)input.LT(1);
+                    match(input,DQ_STRING_LITERAL,FOLLOW_DQ_STRING_LITERAL_in_literal1132); if (failed) return retval;
+                    if ( backtracking==0 ) {
+                    DQ_STRING_LITERAL115_tree = (Object)adaptor.create(DQ_STRING_LITERAL115);
+                    adaptor.addChild(root_0, DQ_STRING_LITERAL115_tree);
+                    }
+
+                    }
+                    break;
+                case 4 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:175:4: boolLiteral
+                    {
+                    root_0 = (Object)adaptor.nil();
+
+                    pushFollow(FOLLOW_boolLiteral_in_literal1137);
+                    boolLiteral116=boolLiteral();
+                    _fsp--;
+                    if (failed) return retval;
+                    if ( backtracking==0 ) adaptor.addChild(root_0, boolLiteral116.getTree());
+
+                    }
+                    break;
+                case 5 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:176:4: NULL_LITERAL
+                    {
+                    root_0 = (Object)adaptor.nil();
+
+                    NULL_LITERAL117=(Token)input.LT(1);
+                    match(input,NULL_LITERAL,FOLLOW_NULL_LITERAL_in_literal1142); if (failed) return retval;
+                    if ( backtracking==0 ) {
+                    NULL_LITERAL117_tree = (Object)adaptor.create(NULL_LITERAL117);
+                    adaptor.addChild(root_0, NULL_LITERAL117_tree);
+                    }
+
+                    }
+                    break;
+                case 6 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:177:4: HEXADECIMAL_INTEGER_LITERAL
+                    {
+                    root_0 = (Object)adaptor.nil();
+
+                    HEXADECIMAL_INTEGER_LITERAL118=(Token)input.LT(1);
+                    match(input,HEXADECIMAL_INTEGER_LITERAL,FOLLOW_HEXADECIMAL_INTEGER_LITERAL_in_literal1147); if (failed) return retval;
+                    if ( backtracking==0 ) {
+                    HEXADECIMAL_INTEGER_LITERAL118_tree = (Object)adaptor.create(HEXADECIMAL_INTEGER_LITERAL118);
+                    adaptor.addChild(root_0, HEXADECIMAL_INTEGER_LITERAL118_tree);
+                    }
+
+                    }
+                    break;
+                case 7 :
+                    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:178:4: REAL_LITERAL
+                    {
+                    root_0 = (Object)adaptor.nil();
+
+                    REAL_LITERAL119=(Token)input.LT(1);
+                    match(input,REAL_LITERAL,FOLLOW_REAL_LITERAL_in_literal1153); if (failed) return retval;
+                    if ( backtracking==0 ) {
+                    REAL_LITERAL119_tree = (Object)adaptor.create(REAL_LITERAL119);
+                    adaptor.addChild(root_0, REAL_LITERAL119_tree);
+                    }
+
+                    }
+                    break;
+
+            }
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end literal
+
+    public static class boolLiteral_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start boolLiteral
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:181:1: boolLiteral : ( TRUE | FALSE );
+    public final boolLiteral_return boolLiteral() throws RecognitionException {
+        boolLiteral_return retval = new boolLiteral_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token set120=null;
+
+        Object set120_tree=null;
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:181:12: ( TRUE | FALSE )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:
+            {
+            root_0 = (Object)adaptor.nil();
+
+            set120=(Token)input.LT(1);
+            if ( (input.LA(1)>=TRUE && input.LA(1)<=FALSE) ) {
+                input.consume();
+                if ( backtracking==0 ) adaptor.addChild(root_0, adaptor.create(set120));
+                errorRecovery=false;failed=false;
+            }
+            else {
+                if (backtracking>0) {failed=true; return retval;}
+                MismatchedSetException mse =
+                    new MismatchedSetException(null,input);
+                recoverFromMismatchedSet(input,mse,FOLLOW_set_in_boolLiteral0);    throw mse;
+            }
+
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end boolLiteral
+
+    public static class relationalOperator_return extends ParserRuleReturnScope {
+        Object tree;
+        public Object getTree() { return tree; }
+    };
+
+    // $ANTLR start relationalOperator
+    // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:188:1: relationalOperator : ( EQUAL | NOT_EQUAL | LESS_THAN | LESS_THAN_OR_EQUAL | GREATER_THAN | GREATER_THAN_OR_EQUAL | INSTANCEOF | BETWEEN | MATCHES );
+    public final relationalOperator_return relationalOperator() throws RecognitionException {
+        relationalOperator_return retval = new relationalOperator_return();
+        retval.start = input.LT(1);
+
+        Object root_0 = null;
+
+        Token set121=null;
+
+        Object set121_tree=null;
+
+        try {
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:189:5: ( EQUAL | NOT_EQUAL | LESS_THAN | LESS_THAN_OR_EQUAL | GREATER_THAN | GREATER_THAN_OR_EQUAL | INSTANCEOF | BETWEEN | MATCHES )
+            // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:
+            {
+            root_0 = (Object)adaptor.nil();
+
+            set121=(Token)input.LT(1);
+            if ( (input.LA(1)>=EQUAL && input.LA(1)<=MATCHES) ) {
+                input.consume();
+                if ( backtracking==0 ) adaptor.addChild(root_0, adaptor.create(set121));
+                errorRecovery=false;failed=false;
+            }
+            else {
+                if (backtracking>0) {failed=true; return retval;}
+                MismatchedSetException mse =
+                    new MismatchedSetException(null,input);
+                recoverFromMismatchedSet(input,mse,FOLLOW_set_in_relationalOperator0);    throw mse;
+            }
+
+
+            }
+
+            retval.stop = input.LT(-1);
+
+            if ( backtracking==0 ) {
+                retval.tree = (Object)adaptor.rulePostProcessing(root_0);
+                adaptor.setTokenBoundaries(retval.tree, retval.start, retval.stop);
+            }
+        }
+
+                catch(RecognitionException e) {
+                        reportError(e);
+                        throw e;
+                }
+        finally {
+        }
+        return retval;
+    }
+    // $ANTLR end relationalOperator
+
+    // $ANTLR start synpred1
+    public final void synpred1_fragment() throws RecognitionException {   
+        // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:111:7: ( POUND ID LPAREN )
+        // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:111:8: POUND ID LPAREN
+        {
+        match(input,POUND,FOLLOW_POUND_in_synpred1642); if (failed) return ;
+        match(input,ID,FOLLOW_ID_in_synpred1644); if (failed) return ;
+        match(input,LPAREN,FOLLOW_LPAREN_in_synpred1646); if (failed) return ;
+
+        }
+    }
+    // $ANTLR end synpred1
+
+    // $ANTLR start synpred2
+    public final void synpred2_fragment() throws RecognitionException {   
+        // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:121:4: ( ID LPAREN )
+        // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:121:5: ID LPAREN
+        {
+        match(input,ID,FOLLOW_ID_in_synpred2726); if (failed) return ;
+        match(input,LPAREN,FOLLOW_LPAREN_in_synpred2728); if (failed) return ;
+
+        }
+    }
+    // $ANTLR end synpred2
+
+    // $ANTLR start synpred4
+    public final void synpred4_fragment() throws RecognitionException {   
+        // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:163:7: ( ID ASSIGN )
+        // /Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g:163:8: ID ASSIGN
+        {
+        match(input,ID,FOLLOW_ID_in_synpred41010); if (failed) return ;
+        match(input,ASSIGN,FOLLOW_ASSIGN_in_synpred41012); if (failed) return ;
+
+        }
+    }
+    // $ANTLR end synpred4
+
+    public final boolean synpred4() {
+        backtracking++;
+        int start = input.mark();
+        try {
+            synpred4_fragment(); // can never throw exception
+        } catch (RecognitionException re) {
+            System.err.println("impossible: "+re);
+        }
+        boolean success = !failed;
+        input.rewind(start);
+        backtracking--;
+        failed=false;
+        return success;
+    }
+    public final boolean synpred2() {
+        backtracking++;
+        int start = input.mark();
+        try {
+            synpred2_fragment(); // can never throw exception
+        } catch (RecognitionException re) {
+            System.err.println("impossible: "+re);
+        }
+        boolean success = !failed;
+        input.rewind(start);
+        backtracking--;
+        failed=false;
+        return success;
+    }
+    public final boolean synpred1() {
+        backtracking++;
+        int start = input.mark();
+        try {
+            synpred1_fragment(); // can never throw exception
+        } catch (RecognitionException re) {
+            System.err.println("impossible: "+re);
+        }
+        boolean success = !failed;
+        input.rewind(start);
+        backtracking--;
+        failed=false;
+        return success;
+    }
+
+
+ 
+
+    public static final BitSet FOLLOW_expression_in_expr130 = new BitSet(new long[]{0x0000000000000000L});
+    public static final BitSet FOLLOW_EOF_in_expr132 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_logicalOrExpression_in_expression152 = new BitSet(new long[]{0x0000000000380002L});
+    public static final BitSet FOLLOW_ASSIGN_in_expression161 = new BitSet(new long[]{0x001FFD5A18800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_logicalOrExpression_in_expression164 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_DEFAULT_in_expression174 = new BitSet(new long[]{0x001FFD5A18800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_logicalOrExpression_in_expression177 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_QMARK_in_expression187 = new BitSet(new long[]{0x001FFD5A18800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_expression_in_expression190 = new BitSet(new long[]{0x0000000000400000L});
+    public static final BitSet FOLLOW_COLON_in_expression192 = new BitSet(new long[]{0x001FFD5A18800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_expression_in_expression195 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_LPAREN_in_parenExpr206 = new BitSet(new long[]{0x001FFD5A18800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_expression_in_parenExpr209 = new BitSet(new long[]{0x0000000001000000L});
+    public static final BitSet FOLLOW_RPAREN_in_parenExpr211 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_logicalAndExpression_in_logicalOrExpression222 = new BitSet(new long[]{0x0000000002000002L});
+    public static final BitSet FOLLOW_OR_in_logicalOrExpression225 = new BitSet(new long[]{0x001FFD5A18800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_logicalAndExpression_in_logicalOrExpression228 = new BitSet(new long[]{0x0000000002000002L});
+    public static final BitSet FOLLOW_relationalExpression_in_logicalAndExpression263 = new BitSet(new long[]{0x0000000004000002L});
+    public static final BitSet FOLLOW_AND_in_logicalAndExpression266 = new BitSet(new long[]{0x001FFD5A18800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_relationalExpression_in_logicalAndExpression269 = new BitSet(new long[]{0x0000000004000002L});
+    public static final BitSet FOLLOW_sumExpression_in_relationalExpression280 = new BitSet(new long[]{0xFF00000000000002L,0x0000000000000001L});
+    public static final BitSet FOLLOW_relationalOperator_in_relationalExpression283 = new BitSet(new long[]{0x001FFD5A18800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_sumExpression_in_relationalExpression286 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_productExpression_in_sumExpression297 = new BitSet(new long[]{0x0000000018000002L});
+    public static final BitSet FOLLOW_PLUS_in_sumExpression302 = new BitSet(new long[]{0x001FFD5A18800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_MINUS_in_sumExpression307 = new BitSet(new long[]{0x001FFD5A18800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_productExpression_in_sumExpression311 = new BitSet(new long[]{0x0000000018000002L});
+    public static final BitSet FOLLOW_powerExpr_in_productExpression322 = new BitSet(new long[]{0x00000000E0000002L});
+    public static final BitSet FOLLOW_STAR_in_productExpression326 = new BitSet(new long[]{0x001FFD5A18800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_DIV_in_productExpression331 = new BitSet(new long[]{0x001FFD5A18800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_MOD_in_productExpression335 = new BitSet(new long[]{0x001FFD5A18800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_powerExpr_in_productExpression339 = new BitSet(new long[]{0x00000000E0000002L});
+    public static final BitSet FOLLOW_unaryExpression_in_powerExpr351 = new BitSet(new long[]{0x0000000100000002L});
+    public static final BitSet FOLLOW_POWER_in_powerExpr354 = new BitSet(new long[]{0x001FFD5A18800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_unaryExpression_in_powerExpr357 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_PLUS_in_unaryExpression371 = new BitSet(new long[]{0x001FFD5A18800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_MINUS_in_unaryExpression376 = new BitSet(new long[]{0x001FFD5A18800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_BANG_in_unaryExpression381 = new BitSet(new long[]{0x001FFD5A18800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_unaryExpression_in_unaryExpression385 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_primaryExpression_in_unaryExpression391 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_startNode_in_primaryExpression405 = new BitSet(new long[]{0x0000004400000002L});
+    public static final BitSet FOLLOW_node_in_primaryExpression408 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_parenExpr_in_startNode441 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_methodOrProperty_in_startNode449 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_functionOrVar_in_startNode458 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_indexer_in_startNode466 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_literal_in_startNode474 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_type_in_startNode482 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_constructor_in_startNode490 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_projection_in_startNode498 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_selection_in_startNode507 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_firstSelection_in_startNode516 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_lastSelection_in_startNode524 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_DOT_in_node544 = new BitSet(new long[]{0x00001D1800000000L});
+    public static final BitSet FOLLOW_dottedNode_in_node546 = new BitSet(new long[]{0x0000004400000002L});
+    public static final BitSet FOLLOW_nonDottedNode_in_node551 = new BitSet(new long[]{0x0000004400000002L});
+    public static final BitSet FOLLOW_indexer_in_nonDottedNode563 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_methodOrProperty_in_dottedNode576 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_functionOrVar_in_dottedNode582 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_projection_in_dottedNode590 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_selection_in_dottedNode599 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_firstSelection_in_dottedNode608 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_lastSelection_in_dottedNode617 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_function_in_functionOrVar651 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_var_in_functionOrVar659 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_POUND_in_function676 = new BitSet(new long[]{0x0000001000000000L});
+    public static final BitSet FOLLOW_ID_in_function680 = new BitSet(new long[]{0x0000000000800000L});
+    public static final BitSet FOLLOW_methodArgs_in_function682 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_POUND_in_var703 = new BitSet(new long[]{0x0000001000000000L});
+    public static final BitSet FOLLOW_ID_in_var707 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_ID_in_methodOrProperty735 = new BitSet(new long[]{0x0000000000800000L});
+    public static final BitSet FOLLOW_methodArgs_in_methodOrProperty737 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_property_in_methodOrProperty751 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_LPAREN_in_methodArgs766 = new BitSet(new long[]{0x001FFD5A19800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_argument_in_methodArgs770 = new BitSet(new long[]{0x0000002001000000L});
+    public static final BitSet FOLLOW_COMMA_in_methodArgs773 = new BitSet(new long[]{0x001FFD5A18800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_argument_in_methodArgs776 = new BitSet(new long[]{0x0000002001000000L});
+    public static final BitSet FOLLOW_COMMA_in_methodArgs781 = new BitSet(new long[]{0x0000000001000000L});
+    public static final BitSet FOLLOW_RPAREN_in_methodArgs788 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_ID_in_property801 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_LBRACKET_in_indexer816 = new BitSet(new long[]{0x001FFD5A18800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_argument_in_indexer820 = new BitSet(new long[]{0x000000A000000000L});
+    public static final BitSet FOLLOW_COMMA_in_indexer823 = new BitSet(new long[]{0x001FFD5A18800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_argument_in_indexer827 = new BitSet(new long[]{0x000000A000000000L});
+    public static final BitSet FOLLOW_RBRACKET_in_indexer831 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_PROJECT_in_projection857 = new BitSet(new long[]{0x001FFD5A18800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_expression_in_projection860 = new BitSet(new long[]{0x0000020000000000L});
+    public static final BitSet FOLLOW_RCURLY_in_projection862 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_SELECT_in_selection870 = new BitSet(new long[]{0x001FFD5A18800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_expression_in_selection873 = new BitSet(new long[]{0x0000020000000000L});
+    public static final BitSet FOLLOW_RCURLY_in_selection875 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_SELECT_FIRST_in_firstSelection883 = new BitSet(new long[]{0x001FFD5A18800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_expression_in_firstSelection886 = new BitSet(new long[]{0x0000020000000000L});
+    public static final BitSet FOLLOW_RCURLY_in_firstSelection888 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_SELECT_LAST_in_lastSelection896 = new BitSet(new long[]{0x001FFD5A18800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_expression_in_lastSelection899 = new BitSet(new long[]{0x0000020000000000L});
+    public static final BitSet FOLLOW_RCURLY_in_lastSelection901 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_TYPE_in_type910 = new BitSet(new long[]{0x0000001000000000L});
+    public static final BitSet FOLLOW_qualifiedId_in_type912 = new BitSet(new long[]{0x0000000001000000L});
+    public static final BitSet FOLLOW_RPAREN_in_type914 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_77_in_constructor945 = new BitSet(new long[]{0x0000001000000000L});
+    public static final BitSet FOLLOW_qualifiedId_in_constructor947 = new BitSet(new long[]{0x0000000000800000L});
+    public static final BitSet FOLLOW_ctorArgs_in_constructor949 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_LPAREN_in_ctorArgs971 = new BitSet(new long[]{0x001FFD5A19800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_namedArgument_in_ctorArgs975 = new BitSet(new long[]{0x0000002001000000L});
+    public static final BitSet FOLLOW_COMMA_in_ctorArgs978 = new BitSet(new long[]{0x001FFD5A18800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_namedArgument_in_ctorArgs981 = new BitSet(new long[]{0x0000002001000000L});
+    public static final BitSet FOLLOW_RPAREN_in_ctorArgs987 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_expression_in_argument996 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_ID_in_namedArgument1019 = new BitSet(new long[]{0x0000000000080000L});
+    public static final BitSet FOLLOW_ASSIGN_in_namedArgument1021 = new BitSet(new long[]{0x001FFD5A18800010L,0x0000000000002000L});
+    public static final BitSet FOLLOW_expression_in_namedArgument1023 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_argument_in_namedArgument1059 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_ID_in_qualifiedId1071 = new BitSet(new long[]{0x0000000400000002L});
+    public static final BitSet FOLLOW_DOT_in_qualifiedId1074 = new BitSet(new long[]{0x0000001000000000L});
+    public static final BitSet FOLLOW_ID_in_qualifiedId1076 = new BitSet(new long[]{0x0000000400000002L});
+    public static final BitSet FOLLOW_ID_in_contextName1095 = new BitSet(new long[]{0x0000000040000002L});
+    public static final BitSet FOLLOW_DIV_in_contextName1098 = new BitSet(new long[]{0x0000001000000000L});
+    public static final BitSet FOLLOW_ID_in_contextName1100 = new BitSet(new long[]{0x0000000040000002L});
+    public static final BitSet FOLLOW_INTEGER_LITERAL_in_literal1121 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_STRING_LITERAL_in_literal1127 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_DQ_STRING_LITERAL_in_literal1132 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_boolLiteral_in_literal1137 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_NULL_LITERAL_in_literal1142 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_HEXADECIMAL_INTEGER_LITERAL_in_literal1147 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_REAL_LITERAL_in_literal1153 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_set_in_boolLiteral0 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_set_in_relationalOperator0 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_POUND_in_synpred1642 = new BitSet(new long[]{0x0000001000000000L});
+    public static final BitSet FOLLOW_ID_in_synpred1644 = new BitSet(new long[]{0x0000000000800000L});
+    public static final BitSet FOLLOW_LPAREN_in_synpred1646 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_ID_in_synpred2726 = new BitSet(new long[]{0x0000000000800000L});
+    public static final BitSet FOLLOW_LPAREN_in_synpred2728 = new BitSet(new long[]{0x0000000000000002L});
+    public static final BitSet FOLLOW_ID_in_synpred41010 = new BitSet(new long[]{0x0000000000080000L});
+    public static final BitSet FOLLOW_ASSIGN_in_synpred41012 = new BitSet(new long[]{0x0000000000000002L});
+
+}

+ 142 - 0
spring-el/src/main/java/org/springframework/expression/spel/generated/SpringExpressions__.g

@@ -0,0 +1,142 @@
+lexer grammar SpringExpressions;
+options {
+  language=Java;
+
+}
+@header {package org.springframework.expression.spel.generated;}
+
+T77 : 'new' ;
+
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 183
+INTEGER_LITERAL
+	: (DECIMAL_DIGIT)+ (INTEGER_TYPE_SUFFIX)?;
+
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 186
+HEXADECIMAL_INTEGER_LITERAL : ('0x' | '0X') (HEX_DIGIT)+ (INTEGER_TYPE_SUFFIX)?;
+
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 200
+ASSIGN: '=';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 201
+EQUAL: '==';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 202
+NOT_EQUAL: '!=';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 203
+LESS_THAN: '<';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 204
+LESS_THAN_OR_EQUAL: '<=';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 205
+GREATER_THAN: '>';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 206
+GREATER_THAN_OR_EQUAL: '>=';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 207
+INSTANCEOF:     'instanceof';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 208
+BETWEEN:'between';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 209
+MATCHES:'matches';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 210
+NULL_LITERAL: 'null';
+
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 212
+SEMI: ';';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 213
+DOT:    '.';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 214
+COMMA:	',';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 215
+LPAREN: '(';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 216
+RPAREN: ')';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 217
+LCURLY: '{';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 218
+RCURLY: '}';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 219
+LBRACKET: '[';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 220
+RBRACKET: ']';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 221
+PIPE:	'|';
+
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 223
+AND:    'and';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 224
+OR:     'or';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 225
+FALSE:  'false';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 226
+TRUE:   'true';
+
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 228
+PLUS: '+';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 229
+MINUS: '-';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 230
+DIV: '/';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 231
+STAR: '*';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 232
+MOD: '%';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 233
+POWER: '^';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 234
+BANG: '!';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 235
+POUND: '#';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 236
+QMARK: '?';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 237
+DEFAULT: '??';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 238
+PROJECT: '!{';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 239
+SELECT: '?{';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 240
+SELECT_FIRST: '^{';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 241
+SELECT_LAST: '${';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 242
+TYPE: 'T(';
+
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 244
+STRING_LITERAL:	'\''! (APOS|~'\'')* '\''!;
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 245
+DQ_STRING_LITERAL:	'"'! (~'"')* '"'!;
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 246
+ID:	('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9'|DOT_ESCAPED)*;
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 247
+DOT_ESCAPED: '\\.';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 248
+WS: ( ' ' | '\t' | '\n' |'\r')+ { $channel=HIDDEN; } ;
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 249
+DOLLAR:	'$';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 250
+AT: '@';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 251
+UPTO: '..';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 252
+COLON: ':';
+
+
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 255
+REAL_LITERAL :	
+  ('.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) |
+	((DECIMAL_DIGIT)+ '.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) |
+	((DECIMAL_DIGIT)+ (EXPONENT_PART) (REAL_TYPE_SUFFIX)?) |
+	((DECIMAL_DIGIT)+ (REAL_TYPE_SUFFIX));
+
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 261
+fragment APOS : '\''! '\'';
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 262
+fragment DECIMAL_DIGIT : '0'..'9' ;
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 263
+fragment INTEGER_TYPE_SUFFIX : ( 'L' | 'l' );
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 264
+fragment HEX_DIGIT : '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'|'A'|'B'|'C'|'D'|'E'|'F'|'a'|'b'|'c'|'d'|'e'|'f';		
+	
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 266
+fragment EXPONENT_PART : 'e'  (SIGN)*  (DECIMAL_DIGIT)+ | 'E'  (SIGN)*  (DECIMAL_DIGIT)+ ;	
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 267
+fragment SIGN :	'+' | '-' ;
+// $ANTLR src "/Users/aclement/el2/spring-framework/trunk/org.springframework.expression/src/main/java/org/springframework/expression/spel/generated/SpringExpressions.g" 268
+fragment REAL_TYPE_SUFFIX : 'F' | 'f' | 'D' | 'd';

+ 39 - 0
spring-el/src/main/java/org/springframework/expression/spel/internal/EmptySpelNode.java

@@ -0,0 +1,39 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.internal;
+
+import org.antlr.runtime.Token;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.ExpressionState;
+import org.springframework.expression.spel.ast.SpelNode;
+
+public class EmptySpelNode extends SpelNode {
+
+	public EmptySpelNode(Token payload) {
+		super(payload);
+	}
+
+	@Override
+	public Object getValue(ExpressionState state) throws SpelException {
+		throw new RuntimeException("?");
+	}
+
+	@Override
+	public String toStringAST() {
+		return "<no string form node '" + getTokenName() + "'>";
+	}
+
+}

+ 35 - 0
spring-el/src/main/java/org/springframework/expression/spel/internal/InternalELException.java

@@ -0,0 +1,35 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.internal;
+
+import org.springframework.expression.spel.SpelException;
+
+/**
+ * Wraps an ELException and can pass up through Antlr since it is unchecked, where it can then be unwrapped.
+ * 
+ * @author Andy Clement
+ */
+public class InternalELException extends RuntimeException {
+
+	public InternalELException(SpelException e) {
+		super(e);
+	}
+
+	@Override
+	public SpelException getCause() {
+		return (SpelException) super.getCause();
+	}
+}

+ 33 - 0
spring-el/src/main/java/org/springframework/expression/spel/internal/KeyValuePair.java

@@ -0,0 +1,33 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.internal;
+
+/**
+ * Special object that is used to wrap a map entry/value when iterating over a map.  Providing a direct way for the 
+ * expression to refer to either the key or value.
+ * 
+ * @author Andy Clement
+ */
+public class KeyValuePair {
+	public Object key;
+	public Object value;
+	
+	public KeyValuePair(Object k, Object v) {
+		this.key = k;
+		this.value = v;
+	}
+
+}

+ 172 - 0
spring-el/src/main/java/org/springframework/expression/spel/internal/SpelTreeAdaptor.java

@@ -0,0 +1,172 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.internal;
+
+import org.antlr.runtime.Token;
+import org.antlr.runtime.tree.CommonTreeAdaptor;
+import org.springframework.expression.spel.ast.Assign;
+import org.springframework.expression.spel.ast.BooleanLiteral;
+import org.springframework.expression.spel.ast.CompoundExpression;
+import org.springframework.expression.spel.ast.ConstructorReference;
+import org.springframework.expression.spel.ast.Dot;
+import org.springframework.expression.spel.ast.FunctionReference;
+import org.springframework.expression.spel.ast.Identifier;
+import org.springframework.expression.spel.ast.Indexer;
+import org.springframework.expression.spel.ast.Literal;
+import org.springframework.expression.spel.ast.MethodReference;
+import org.springframework.expression.spel.ast.NullLiteral;
+import org.springframework.expression.spel.ast.OperatorAnd;
+import org.springframework.expression.spel.ast.OperatorBetween;
+import org.springframework.expression.spel.ast.OperatorDivide;
+import org.springframework.expression.spel.ast.OperatorEquality;
+import org.springframework.expression.spel.ast.OperatorGreaterThan;
+import org.springframework.expression.spel.ast.OperatorGreaterThanOrEqual;
+import org.springframework.expression.spel.ast.OperatorInequality;
+import org.springframework.expression.spel.ast.OperatorInstanceof;
+import org.springframework.expression.spel.ast.OperatorLessThan;
+import org.springframework.expression.spel.ast.OperatorLessThanOrEqual;
+import org.springframework.expression.spel.ast.OperatorMatches;
+import org.springframework.expression.spel.ast.OperatorMinus;
+import org.springframework.expression.spel.ast.OperatorModulus;
+import org.springframework.expression.spel.ast.OperatorMultiply;
+import org.springframework.expression.spel.ast.OperatorNot;
+import org.springframework.expression.spel.ast.OperatorOr;
+import org.springframework.expression.spel.ast.OperatorPlus;
+import org.springframework.expression.spel.ast.Placeholder;
+import org.springframework.expression.spel.ast.Projection;
+import org.springframework.expression.spel.ast.PropertyOrFieldReference;
+import org.springframework.expression.spel.ast.QualifiedIdentifier;
+import org.springframework.expression.spel.ast.RealLiteral;
+import org.springframework.expression.spel.ast.Selection;
+import org.springframework.expression.spel.ast.StringLiteral;
+import org.springframework.expression.spel.ast.Ternary;
+import org.springframework.expression.spel.ast.TypeReference;
+import org.springframework.expression.spel.ast.VariableReference;
+import org.springframework.expression.spel.generated.SpringExpressionsLexer;
+
+public class SpelTreeAdaptor extends CommonTreeAdaptor {
+	@Override
+	public Object create(Token payload) {
+		if (payload != null) {
+			switch (payload.getType()) {
+
+			case SpringExpressionsLexer.TRUE:
+				return new BooleanLiteral(payload, true);
+			case SpringExpressionsLexer.FALSE:
+				return new BooleanLiteral(payload, false);
+
+			case SpringExpressionsLexer.OR:
+				return new OperatorOr(payload);
+			case SpringExpressionsLexer.AND:
+				return new OperatorAnd(payload);
+			case SpringExpressionsLexer.BANG:
+				return new OperatorNot(payload);
+
+			case SpringExpressionsLexer.REAL_LITERAL:
+				return new RealLiteral(payload);
+			case SpringExpressionsLexer.INTEGER_LITERAL:
+				return Literal.getIntLiteral(payload, 10);
+			case SpringExpressionsLexer.HEXADECIMAL_INTEGER_LITERAL:
+				return Literal.getIntLiteral(payload, 16);
+
+			case SpringExpressionsLexer.NOT_EQUAL:
+				return new OperatorInequality(payload);
+			case SpringExpressionsLexer.EQUAL:
+				return new OperatorEquality(payload);
+			case SpringExpressionsLexer.GREATER_THAN:
+				return new OperatorGreaterThan(payload);
+			case SpringExpressionsLexer.LESS_THAN:
+				return new OperatorLessThan(payload);
+			case SpringExpressionsLexer.LESS_THAN_OR_EQUAL:
+				return new OperatorLessThanOrEqual(payload);
+			case SpringExpressionsLexer.GREATER_THAN_OR_EQUAL:
+				return new OperatorGreaterThanOrEqual(payload);
+			case SpringExpressionsLexer.PLUS:
+				return new OperatorPlus(payload);
+			case SpringExpressionsLexer.MINUS:
+				return new OperatorMinus(payload);
+			case SpringExpressionsLexer.STAR/* MULTIPLY */:
+				return new OperatorMultiply(payload);
+			case SpringExpressionsLexer.DIV/* DIVIDE */:
+				return new OperatorDivide(payload);
+			case SpringExpressionsLexer.MOD:
+				return new OperatorModulus(payload);
+
+			case SpringExpressionsLexer.STRING_LITERAL:
+			case SpringExpressionsLexer.DQ_STRING_LITERAL:
+				return new StringLiteral(payload);
+			case SpringExpressionsLexer.NULL_LITERAL:
+				return new NullLiteral(payload);
+
+			case SpringExpressionsLexer.ID:
+				return new Identifier(payload);
+			case SpringExpressionsLexer.PROPERTY_OR_FIELD:
+				return new PropertyOrFieldReference(payload);
+			case SpringExpressionsLexer.METHOD:
+				return new MethodReference(payload);
+			case SpringExpressionsLexer.QUALIFIED_IDENTIFIER:
+				return new QualifiedIdentifier(payload);
+			case SpringExpressionsLexer.TYPEREF:
+				return new TypeReference(payload);
+
+			case SpringExpressionsLexer.EXPRESSION:
+				return new CompoundExpression(payload);
+
+			case SpringExpressionsLexer.CONSTRUCTOR:
+				return new ConstructorReference(payload, false);
+			case SpringExpressionsLexer.VARIABLEREF:
+				return new VariableReference(payload);
+			case SpringExpressionsLexer.FUNCTIONREF:
+				return new FunctionReference(payload);
+			case SpringExpressionsLexer.PROJECT:
+				return new Projection(payload);
+			case SpringExpressionsLexer.SELECT:
+				return new Selection(payload, Selection.ALL);
+			case SpringExpressionsLexer.SELECT_FIRST:
+				return new Selection(payload, Selection.FIRST);
+			case SpringExpressionsLexer.SELECT_LAST:
+				return new Selection(payload, Selection.LAST);
+
+			case SpringExpressionsLexer.ASSIGN:
+				return new Assign(payload);
+			case SpringExpressionsLexer.QMARK:
+				return new Ternary(payload);
+			case SpringExpressionsLexer.INDEXER:
+				return new Indexer(payload);
+
+			case SpringExpressionsLexer.BETWEEN:
+				return new OperatorBetween(payload);
+			case SpringExpressionsLexer.MATCHES:
+				return new OperatorMatches(payload);
+			case SpringExpressionsLexer.INSTANCEOF:
+				return new OperatorInstanceof(payload);
+
+			case SpringExpressionsLexer.RPAREN:
+				return new Placeholder(payload);
+			case SpringExpressionsLexer.COLON:
+				return new Placeholder(payload);
+
+			case SpringExpressionsLexer.DOT:
+				return new Dot(payload);
+
+			default:
+				throw new RuntimeException("Not implemented for '" + payload + "' " + getToken(payload) + "'   "
+						+ payload.getType());
+			}
+		}
+		return new EmptySpelNode(payload);
+	}
+}

+ 81 - 0
spring-el/src/main/java/org/springframework/expression/spel/internal/SpringExpressionsLexerExtender.java

@@ -0,0 +1,81 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.internal;
+
+import org.antlr.runtime.RecognitionException;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.SpelMessages;
+import org.springframework.expression.spel.generated.SpringExpressionsLexer;
+
+public class SpringExpressionsLexerExtender extends SpringExpressionsLexer {
+
+	public SpringExpressionsLexerExtender() {
+		super();
+	}
+
+	/**
+	 * recover() attempts to provide better error messages once something has gone wrong. It then throws a
+	 * InternalELException (has to be this unchecked exception as the exception must flow through Antlr lexer methods
+	 * that do not have declared exceptions). The InternalELException will be caught at the top level and altered to
+	 * include context (line,column) information before being rethrown.<br>
+	 * 
+	 * This error analysis code is in recover() rather than reportError() because reportError() isn't always called by
+	 * the lexer and there is no way to add the calls to it by editing the .g file.
+	 */
+	@Override
+	public void recover(RecognitionException re) {
+		// TODO recovery needs an overhaul once the expression language syntax is agreed
+		
+		// List<?> rules = getRuleInvocationStack(re, SpringExpressionsLexer.class.getName());
+		// String failedRule = (String) rules.get(rules.size() - 1);
+		// System.out.println("DBG: lexer rule " + failedRule);
+		// need a concrete example of error recovery in here please! then i can delete the below
+		// if (re instanceof NoViableAltException) {
+		// NoViableAltException nvae = (NoViableAltException) re;
+		// // example error data: { "abc": def }
+		// if (failedRule.equals("mTokens") && Character.isLetter((char) (nvae.getUnexpectedType()))) {
+		// logger.error(ParserMessage.ERROR_STRINGS_MUST_BE_QUOTED, re.line, re.charPositionInLine);
+		// }
+		//
+		// } else if (re instanceof MismatchedRangeException) {
+		// // MismatchedRangeException mre = (MismatchedRangeException) re;
+		// // example error data: [ 123e ]
+		// if (failedRule.equals("mDIGIT") && rules.size() > 3 && ((String) rules.get(rules.size() -
+		// 3)).equals("mExponent")) {
+		// logger.error(ParserMessage.ERROR_INVALID_EXPONENT, re.line, re.charPositionInLine);
+		// }
+		// } else if (re instanceof MismatchedTokenException) {
+		// MismatchedTokenException mte = (MismatchedTokenException) re;
+		// logger.error(ParserMessage.ERROR_MISMATCHED_CHARACTER, mte.charPositionInLine, mte.charPositionInLine,
+		// getCharErrorDisplay(mte.expecting), getCharErrorDisplay(mte.c));
+		// }
+		SpelException realException = new SpelException(re, SpelMessages.RECOGNITION_ERROR, re.toString());
+		throw new InternalELException(realException);
+	}
+
+	@Override
+	public void reportError(RecognitionException re) {
+		// Do not report anything. If better messages could be reported they will have been reported
+		// by the recover() method above.
+	}
+
+//	private String getTokenForId(int id) {
+//		if (id == -1)
+//			return "EOF";
+//		return getTokenNames()[id];
+//	}
+
+}

+ 87 - 0
spring-el/src/main/java/org/springframework/expression/spel/internal/SpringExpressionsParserExtender.java

@@ -0,0 +1,87 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.internal;
+
+import org.antlr.runtime.BitSet;
+import org.antlr.runtime.IntStream;
+import org.antlr.runtime.RecognitionException;
+import org.antlr.runtime.Token;
+import org.antlr.runtime.TokenStream;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.SpelMessages;
+import org.springframework.expression.spel.generated.SpringExpressionsParser;
+
+public class SpringExpressionsParserExtender extends SpringExpressionsParser {
+
+	public SpringExpressionsParserExtender(TokenStream input) {
+		super(input);
+	}
+
+	/**
+	 * Override super type implementation and just include the character position rather than the line number since the
+	 * expressions are nearly all going to be just one line.
+	 */
+	@Override
+	public String getErrorHeader(RecognitionException e) {
+		StringBuilder retval = new StringBuilder();
+		retval.append("(pos ").append(e.charPositionInLine).append("): ");
+		return retval.toString();
+	}
+
+	@Override
+	public void displayRecognitionError(String[] tokenNames, RecognitionException e) {
+		String message = getErrorMessage(e, tokenNames);
+		// TODO would something like this be worthwhile to improve messages?
+		// if (message.equals("no viable alternative at input '<EOF>'") && !paraphrase.isEmpty()) {
+		// // This means we ran out of input building something, that something is named in paraphrase
+		// message = "no more input data to process whilst constructing " + paraphrase.peek();
+		// }
+		SpelException parsingProblem = new SpelException(e.charPositionInLine, e, SpelMessages.PARSE_PROBLEM, message);
+		throw new InternalELException(parsingProblem);
+	}
+
+	/**
+	 * Overridden purely because the base implementation does a System.err.println()
+	 */
+	@Override
+	public void recoverFromMismatchedToken(IntStream input, RecognitionException e, int ttype, BitSet follow)
+			throws RecognitionException {
+		// if next token is what we are looking for then "delete" this token
+		if (input.LA(2) == ttype) {
+			reportError(e);
+			/*
+			 * System.err.println("recoverFromMismatchedToken deleting "+input.LT(1)+ " since "+input.LT(2)+" is what we
+			 * want");
+			 */
+			beginResync();
+			input.consume(); // simply delete extra token
+			endResync();
+			input.consume(); // move past ttype token as if all were ok
+			return;
+		}
+		if (!recoverFromMismatchedElement(input, e, follow)) {
+			throw e;
+		}
+	}
+
+	@Override
+	public String getTokenErrorDisplay(Token t) {
+		if (t == null) {
+			return "<unknown>";
+		}
+		return super.getTokenErrorDisplay(t);
+	}
+}

+ 103 - 0
spring-el/src/main/java/org/springframework/expression/spel/internal/TypeCode.java

@@ -0,0 +1,103 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.internal;
+
+public enum TypeCode {
+
+	OBJECT(0, Object.class), BOOLEAN(1, Boolean.TYPE), BYTE(1, Byte.TYPE), CHAR(1, Character.TYPE), SHORT(2, Short.TYPE), INT(
+			3, Integer.TYPE), LONG(4, Long.TYPE), FLOAT(5, Float.TYPE), DOUBLE(6, Double.TYPE);
+
+	private int code;
+	private Class<?> type;
+
+	TypeCode(int code, Class<?> type) {
+		this.code = code;
+		this.type = type;
+	}
+
+	public Class<?> getType() {
+		return type;
+	}
+
+	public static TypeCode forClass(Class<?> c) {
+		TypeCode[] allValues = TypeCode.values();
+		for (int i = 0; i < allValues.length; i++) {
+			TypeCode typeCode = allValues[i];
+			if (c == typeCode.getType()) {
+				return typeCode;
+			}
+		}
+		return OBJECT;
+	}
+
+	/**
+	 * For a primitive name this will determine the typecode value - supports
+	 * int,byte,char,short,long,double,float,boolean
+	 */
+	public static TypeCode forName(String name) {
+		if (name.equals("int"))
+			return TypeCode.INT;
+		else if (name.equals("boolean"))
+			return TypeCode.BOOLEAN;
+		else if (name.equals("char"))
+			return TypeCode.CHAR;
+		else if (name.equals("long"))
+			return TypeCode.LONG;
+		else if (name.equals("float"))
+			return TypeCode.FLOAT;
+		else if (name.equals("double"))
+			return TypeCode.DOUBLE;
+		else if (name.equals("short"))
+			return TypeCode.SHORT;
+		else if (name.equals("byte"))
+			return TypeCode.BYTE;
+		return TypeCode.OBJECT;
+	}
+
+	public int getCode() {
+		return code;
+	}
+
+	public Object coerce(TypeCode fromTypeCode, Object fromObject) {
+		if (this == TypeCode.INT) {
+			switch (fromTypeCode) {
+			case BOOLEAN:
+				return ((Boolean) fromObject).booleanValue() ? 1 : 0;
+			}
+		}
+		//			
+		// return Integer.valueOf
+		// } else if (this==TypeCode.BOOLEAN) {
+		// return new Boolean(left).intValue();
+		return null;
+	}
+
+	public static TypeCode forValue(Number op1) {
+		return forClass(op1.getClass());
+	}
+
+	public boolean isDouble() {
+		return this == DOUBLE;
+	}
+
+	public boolean isFloat() {
+		return this == FLOAT;
+	}
+
+	public boolean isLong() {
+		return this == LONG;
+	}
+}

+ 76 - 0
spring-el/src/main/java/org/springframework/expression/spel/internal/Utils.java

@@ -0,0 +1,76 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.internal;
+
+/**
+ * Utility methods (formatters, etc) used during parsing and evaluation.
+ * 
+ * @author Andy Clement
+ */
+public class Utils {
+
+	/**
+	 * Produce a nice string for a given method name with specified arguments.
+	 * 
+	 * @param name the name of the method
+	 * @param argumentTypes the types of the arguments to the method
+	 * @return nicely formatted string, eg. foo(String,int)
+	 */
+	public static String formatMethodForMessage(String name, Class<?>... argumentTypes) {
+		StringBuilder sb = new StringBuilder();
+		sb.append(name);
+		sb.append("(");
+		if (argumentTypes != null) {
+			for (int i = 0; i < argumentTypes.length; i++) {
+				if (i > 0)
+					sb.append(",");
+				sb.append(argumentTypes[i].getName());
+			}
+		}
+		sb.append(")");
+		return sb.toString();
+	}
+
+	/**
+	 * Produce a nice string for a given class object. For example a string array will have the formatted name
+	 * "java.lang.String[]".
+	 * 
+	 * @param clazz The class whose name is to be formatted
+	 * @return a formatted string suitable for message inclusion
+	 */
+	public static String formatClassnameForMessage(Class<?> clazz) {
+		if (clazz==null) { 
+			return "null";
+		}
+		StringBuilder fmtd = new StringBuilder();
+		if (clazz.isArray()) {
+			int dims = 1;
+			Class baseClass = clazz.getComponentType();
+			while (baseClass.isArray()) {
+				baseClass = baseClass.getComponentType();
+				dims++;
+			}
+			fmtd.append(baseClass.getName());
+			for (int i = 0; i < dims; i++) {
+				fmtd.append("[]");
+			}
+		} else {
+			fmtd.append(clazz.getName());
+		}
+		return fmtd.toString();
+	}
+
+}

+ 57 - 0
spring-el/src/main/java/org/springframework/expression/spel/internal/VariableScope.java

@@ -0,0 +1,57 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.internal;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A new scope is entered when a function is called and it is used to hold the parameters to the function call.  If the names
+ * of the parameters clash with those in a higher level scope, those in the higher level scope will not be accessible whilst
+ * the function is executing.  When the function returns the scope is exited.
+ * 
+ * @author Andy Clement
+ *
+ */
+public class VariableScope {
+
+	private final Map<String, Object> vars = new HashMap<String, Object>();
+
+	public VariableScope() { }
+
+	public VariableScope(Map<String, Object> arguments) {
+		if (arguments!=null) {
+			vars.putAll(arguments);
+		}
+	}
+	
+	public VariableScope(String name,Object value) {
+		vars.put(name,value);
+	}
+
+	public Object lookupVariable(String name) {
+		return vars.get(name);
+	}
+
+	public void setVariable(String name, Object value) {
+		vars.put(name,value);
+	}
+
+	public boolean definesVariable(String name) {
+		return vars.containsKey(name);
+	}
+
+}

+ 35 - 0
spring-el/src/main/java/org/springframework/expression/spel/internal/WrappedExpressionException.java

@@ -0,0 +1,35 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.internal;
+
+import org.springframework.expression.spel.SpelException;
+
+/**
+ * Wraps an ELException and can pass up through Antlr since it is unchecked, where it can then be unwrapped.
+ * 
+ * @author Andy Clement
+ */
+public class WrappedExpressionException extends RuntimeException {
+
+	WrappedExpressionException(SpelException e) {
+		super(e);
+	}
+
+	@Override
+	public SpelException getCause() {
+		return (SpelException) super.getCause();
+	}
+}

+ 74 - 0
spring-el/src/main/java/org/springframework/expression/spel/reflection/ReflectionConstructorExecutor.java

@@ -0,0 +1,74 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.reflection;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+
+import org.springframework.expression.AccessException;
+import org.springframework.expression.ConstructorExecutor;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.EvaluationException;
+
+/**
+ * A simple CommandExecutor implementation that runs a constructor using reflective invocation.
+ * 
+ * @author Andy Clement
+ */
+public class ReflectionConstructorExecutor implements ConstructorExecutor {
+
+	private final Constructor<?> c;
+
+	// When the constructor was found, we will have determined if arguments need to be converted for it
+	// to be invoked. Conversion won't be cheap so let's only do it if necessary.
+	private final Integer[] argsRequiringConversion;
+
+	public ReflectionConstructorExecutor(Constructor<?> constructor, Integer[] argsRequiringConversion) {
+		c = constructor;
+		this.argsRequiringConversion = argsRequiringConversion;
+	}
+
+	/**
+	 * Invoke a constructor via reflection.
+	 */
+	public Object execute(EvaluationContext context, Object... arguments) throws AccessException {
+		if (argsRequiringConversion != null && arguments != null) {
+			try {
+				ReflectionUtils.convertArguments(c.getParameterTypes(), c.isVarArgs(), context.getTypeUtils()
+						.getTypeConverter(), argsRequiringConversion, arguments);
+			} catch (EvaluationException ex) {
+				throw new AccessException("Problem invoking constructor on '" + c + "': " + ex.getMessage(), ex);
+			}
+		}
+		if (c.isVarArgs()) {
+			arguments = ReflectionUtils.setupArgumentsForVarargsInvocation(c.getParameterTypes(), arguments);
+		}
+		try {
+			if (!c.isAccessible()) {
+				c.setAccessible(true);
+			}
+			return c.newInstance(arguments);
+		} catch (IllegalArgumentException e) {
+			throw new AccessException("Problem invoking constructor on '" + c + "' : " + e.getMessage(), e);
+		} catch (InstantiationException e) {
+			throw new AccessException("Problem invoking constructor on '" + c + "' : " + e.getMessage(), e);
+		} catch (IllegalAccessException e) {
+			throw new AccessException("Problem invoking constructor on '" + c + "' : " + e.getMessage(), e);
+		} catch (InvocationTargetException e) {
+			throw new AccessException("Problem invoking constructor on '" + c + "' : " + e.getMessage(), e);
+		}
+	}
+}

+ 66 - 0
spring-el/src/main/java/org/springframework/expression/spel/reflection/ReflectionConstructorResolver.java

@@ -0,0 +1,66 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.reflection;
+
+import org.springframework.expression.AccessException;
+import org.springframework.expression.ConstructorExecutor;
+import org.springframework.expression.ConstructorResolver;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.spel.reflection.ReflectionUtils.DiscoveredConstructor;
+
+/**
+ * A constructor resolver that uses reflection to locate the constructor that should be invoked
+ * 
+ * @author Andy Clement
+ */
+public class ReflectionConstructorResolver implements ConstructorResolver {
+
+	/*
+	 * Indicates if this resolve will allow matches to be found that require some of the input arguments to be
+	 * transformed by the conversion service.
+	 */
+	private boolean allowMatchesRequiringArgumentConversion = true;
+
+	public ReflectionConstructorResolver() {
+	}
+
+	public ReflectionConstructorResolver(boolean allowMatchesRequiringArgumentConversion) {
+		this.allowMatchesRequiringArgumentConversion = allowMatchesRequiringArgumentConversion;
+	}
+
+	public void setAllowMatchRequiringArgumentConversion(boolean allow) {
+		this.allowMatchesRequiringArgumentConversion = allow;
+	}
+
+	/**
+	 * Locate a matching constructor or return null if non can be found.
+	 */
+	public ConstructorExecutor resolve(EvaluationContext context, String typename, Class<?>[] argumentTypes)
+			throws AccessException {
+		try {
+			Class<?> c = context.getTypeUtils().getTypeLocator().findType(typename);
+			DiscoveredConstructor dCtor = ReflectionUtils.findConstructor(context.getTypeUtils().getTypeConverter(), c,
+					argumentTypes, allowMatchesRequiringArgumentConversion);
+			if (dCtor == null) {
+				return null;
+			}
+			return new ReflectionConstructorExecutor(dCtor.theConstructor, dCtor.argumentsRequiringConversion);
+		} catch (EvaluationException e) {
+			throw new AccessException(null,e);
+		}
+	}
+}

+ 70 - 0
spring-el/src/main/java/org/springframework/expression/spel/reflection/ReflectionMethodExecutor.java

@@ -0,0 +1,70 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.reflection;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.springframework.expression.AccessException;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.MethodExecutor;
+
+public class ReflectionMethodExecutor implements MethodExecutor {
+
+	private final Method m;
+
+	// When the method was found, we will have determined if arguments need to be converted for it
+	// to be invoked. Conversion won't be cheap so let's only do it if necessary.
+	private final Integer[] argsRequiringConversion;
+
+	public ReflectionMethodExecutor(Method theMethod, Integer[] argumentsRequiringConversion) {
+		m = theMethod;
+		argsRequiringConversion = argumentsRequiringConversion;
+	}
+
+	public Object execute(EvaluationContext context, Object target, Object... arguments) throws AccessException {
+		if (argsRequiringConversion != null && arguments != null) {
+			try {
+				ReflectionUtils.convertArguments(m.getParameterTypes(), m.isVarArgs(), context.getTypeUtils()
+						.getTypeConverter(), argsRequiringConversion, arguments);
+			} catch (EvaluationException ex) {
+				throw new AccessException("Problem invoking method '" + m.getName() + "' on '" + target.getClass()
+						+ "': " + ex.getMessage(), ex);
+			}
+		}
+		if (m.isVarArgs()) {
+			arguments = ReflectionUtils.setupArgumentsForVarargsInvocation(m.getParameterTypes(), arguments);
+		}
+		try {
+			if (!m.isAccessible()) {
+				m.setAccessible(true);
+			}
+			return m.invoke(target, arguments);
+		} catch (IllegalArgumentException e) {
+			throw new AccessException("Problem invoking method '" + m.getName() + "' on '" + target.getClass() + "': "
+					+ e.getMessage(), e);
+		} catch (IllegalAccessException e) {
+			throw new AccessException("Problem invoking method '" + m.getName() + "' on '" + target.getClass() + "': "
+					+ e.getMessage(), e);
+		} catch (InvocationTargetException e) {
+			e.getCause().printStackTrace();
+			throw new AccessException("Problem invoking method '" + m.getName() + "' on '" + target.getClass() + "': "
+					+ e.getMessage(), e);
+		}
+	}
+
+}

+ 63 - 0
spring-el/src/main/java/org/springframework/expression/spel/reflection/ReflectionMethodResolver.java

@@ -0,0 +1,63 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.reflection;
+
+import org.springframework.expression.AccessException;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.MethodExecutor;
+import org.springframework.expression.MethodResolver;
+import org.springframework.expression.spel.reflection.ReflectionUtils.DiscoveredMethod;
+
+/**
+ * A method resolver that uses reflection to locate the method that should be invoked
+ * 
+ * @author Andy Clement
+ */
+public class ReflectionMethodResolver implements MethodResolver {
+
+	/*
+	 * Indicates if this resolve will allow matches to be found that require some of the input arguments to be
+	 * transformed by the conversion service.
+	 */
+	private boolean allowMatchesRequiringArgumentConversion = true;
+
+	public ReflectionMethodResolver() {
+	}
+
+	public ReflectionMethodResolver(boolean allowMatchesRequiringArgumentConversion) {
+		this.allowMatchesRequiringArgumentConversion = allowMatchesRequiringArgumentConversion;
+	}
+
+	public void setAllowMatchRequiringArgumentConversion(boolean allow) {
+		this.allowMatchesRequiringArgumentConversion = allow;
+	}
+
+	public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name, Class<?>[] argumentTypes) throws AccessException {
+		try {
+			Class<?> relevantClass = (targetObject instanceof Class ? (Class<?>) targetObject : targetObject.getClass());
+			DiscoveredMethod dMethod = ReflectionUtils.findMethod(context.getTypeUtils().getTypeConverter(), name,
+					argumentTypes, relevantClass, allowMatchesRequiringArgumentConversion);
+			if (dMethod == null) {
+				return null;
+			}
+			return new ReflectionMethodExecutor(dMethod.theMethod, dMethod.argumentsRequiringConversion);
+		} catch (EvaluationException e) {
+			throw new AccessException(null,e);
+		}
+	}
+
+}

+ 57 - 0
spring-el/src/main/java/org/springframework/expression/spel/reflection/ReflectionPropertyReaderExecutor.java

@@ -0,0 +1,57 @@
+package org.springframework.expression.spel.reflection;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.springframework.expression.AccessException;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.PropertyReaderExecutor;
+
+public class ReflectionPropertyReaderExecutor implements PropertyReaderExecutor {
+
+	private Method methodToAccessProperty;
+	private Field fieldToAccessProperty;
+	private final String propertyName;
+
+	public ReflectionPropertyReaderExecutor(String propertyName, Method method) {
+		this.propertyName = propertyName;
+		methodToAccessProperty = method;
+	}
+
+	public ReflectionPropertyReaderExecutor(String propertyName, Field field) {
+		this.propertyName = propertyName;
+		fieldToAccessProperty = field;
+	}
+
+	public Object execute(EvaluationContext context, Object target) throws AccessException {
+		if (methodToAccessProperty != null) {
+			try {
+				if (!methodToAccessProperty.isAccessible()) {
+					methodToAccessProperty.setAccessible(true);
+				}
+				return methodToAccessProperty.invoke(target);
+			} catch (IllegalArgumentException e) {
+				throw new AccessException("Unable to access property '" + propertyName + "' through getter", e);
+			} catch (IllegalAccessException e) {
+				throw new AccessException("Unable to access property '" + propertyName + "' through getter", e);
+			} catch (InvocationTargetException e) {
+				throw new AccessException("Unable to access property '" + propertyName + "' through getter", e);
+			}
+		}
+		if (fieldToAccessProperty != null) {
+			try {
+				if (!fieldToAccessProperty.isAccessible()) {
+					fieldToAccessProperty.setAccessible(true);
+				}
+				return fieldToAccessProperty.get(target);
+			} catch (IllegalArgumentException e) {
+				throw new AccessException("Unable to access field: " + propertyName, e);
+			} catch (IllegalAccessException e) {
+				throw new AccessException("Unable to access field: " + propertyName, e);
+			}
+		}
+		throw new AccessException("No method or field accessor found for property '" + propertyName + "'");
+	}
+
+}

+ 21 - 0
spring-el/src/main/java/org/springframework/expression/spel/reflection/ReflectionPropertyReaderExecutorForArrayLength.java

@@ -0,0 +1,21 @@
+package org.springframework.expression.spel.reflection;
+
+import java.lang.reflect.Array;
+
+import org.springframework.expression.AccessException;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.PropertyReaderExecutor;
+
+public class ReflectionPropertyReaderExecutorForArrayLength implements PropertyReaderExecutor {
+
+	public ReflectionPropertyReaderExecutorForArrayLength() {
+	}
+
+	public Object execute(EvaluationContext context, Object target) throws AccessException {
+		if (target.getClass().isArray()) {
+			return Array.getLength(target);
+		}
+		throw new AccessException("Cannot determine length of a non-array type  '" + target.getClass() + "'");
+	}
+
+}

+ 223 - 0
spring-el/src/main/java/org/springframework/expression/spel/reflection/ReflectionPropertyResolver.java

@@ -0,0 +1,223 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.reflection;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import org.springframework.expression.CacheablePropertyAccessor;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.PropertyReaderExecutor;
+import org.springframework.expression.PropertyWriterExecutor;
+
+/**
+ * Simple PropertyResolver that uses reflection to access properties for reading and writing. A property can be accessed
+ * if it is accessible as a field on the object or through a getter (if being read) or a setter (if being written). This
+ * implementation currently follows the Resolver/Executor model (it extends CacheablePropertyAccessor) - the code that
+ * would be used if it were a simple property accessor is shown at the end.
+ * 
+ * @author Andy Clement
+ */
+public class ReflectionPropertyResolver extends CacheablePropertyAccessor {
+
+	/**
+	 * @return null which means this is a general purpose accessor
+	 */
+	public Class<?>[] getSpecificTargetClasses() {
+		return null;
+	}
+
+	/**
+	 * Use reflection to discover if a named property is accessible on an target type and if it is return an executor
+	 * object that can be called repeatedly to retrieve that property. A property is accessible either as a field or
+	 * through a getter.
+	 * 
+	 * @param context the context in which the access is being attempted
+	 * @param target the target object on which the property is being accessed
+	 * @param name the name of the property
+	 */
+	@Override
+	public PropertyReaderExecutor getReaderAccessor(EvaluationContext context, Object target, Object name) {
+		if (target == null) {
+			return null;
+		}
+		Class<?> relevantClass = (target instanceof Class ? (Class<?>) target : target.getClass());
+		if (!(name instanceof String)) {
+			// A property not found exception will occur if the reflection finder was supposed to find it
+			return null;
+		}
+		String propertyName = (String) name;
+		if (relevantClass.isArray() && propertyName.equals("length")) {
+			return new ReflectionPropertyReaderExecutorForArrayLength();
+		}
+		Method m = ReflectionUtils.findGetterForProperty(propertyName, relevantClass, target instanceof Class);
+		if (m != null) {
+			return new ReflectionPropertyReaderExecutor(propertyName, m);
+		}
+		Field field = ReflectionUtils.findField(propertyName, relevantClass, target instanceof Class);
+		if (field != null) {
+			return new ReflectionPropertyReaderExecutor(propertyName, field);
+		}
+		return null;
+	}
+
+	/**
+	 * Use reflection to discover if a named property is accessible on an target type and if it is return an executor
+	 * object that can be called repeatedly to set that property. A property is writable either as a field or through a
+	 * setter.
+	 * 
+	 * @param context the context in which the set is being attempted
+	 * @param target the target object on which the property is being set
+	 * @param name the name of the property
+	 */
+	@Override
+	public PropertyWriterExecutor getWriterAccessor(EvaluationContext context, Object target, Object name) {
+		if (target == null) {
+			return null;
+		}
+		Class<?> relevantClass = (target instanceof Class ? (Class<?>) target : target.getClass());
+		if (!(name instanceof String)) {
+			// A property not found exception will occur if the reflection finder was supposed to find it
+			return null;
+		}
+		Method m = ReflectionUtils.findSetterForProperty((String) name, relevantClass, target instanceof Class);
+		if (m != null) {
+			return new ReflectionPropertyWriterExecutor((String) name, m);
+		}
+		Field field = ReflectionUtils.findField((String) name, relevantClass, target instanceof Class);
+		if (field != null) {
+			return new ReflectionPropertyWriterExecutor((String) name, field);
+		}
+		return null;
+	}
+
+	// /**
+	// * Return true if the resolver is able to read the specified property from the specified target.
+	// */
+	// public boolean canRead(EvaluationContext relatedContext, Object target, Object name) throws AccessException {
+	// if (target==null) {
+	// return false;
+	// }
+	// Class<?> relevantClass = (target instanceof Class ? (Class<?>) target : target.getClass());
+	// if (!(name instanceof String)) {
+	// return false;
+	// }
+	// String propertyName = (String) name;
+	// Field field = ReflectionUtils.findField(propertyName, relevantClass);
+	// if (field != null) {
+	// return true;
+	// }
+	// Method m = ReflectionUtils.findGetterForProperty(propertyName, relevantClass);
+	// if (m != null) {
+	// return true;
+	// }
+	// return false;
+	// }
+	//
+	// /**
+	// * Read the specified property from the specified target. //
+	// */
+	// public Object read(EvaluationContext context, Object target, Object name) throws AccessException {
+	// if (target==null) {
+	// return null;
+	// }
+	// Class<?> relevantClass = (target instanceof Class ? (Class<?>) target : target.getClass());
+	// if (!(name instanceof String)) {
+	// return null;
+	// }
+	// String propertyName = (String) name;
+	// Field field = ReflectionUtils.findField(propertyName, relevantClass);
+	// if (field != null) {
+	// try {
+	// if (!field.isAccessible()) {
+	// field.setAccessible(true);
+	// }
+	// return field.get(target);
+	// } catch (IllegalArgumentException e) {
+	// throw new AccessException("Unable to access field: " + name, e);
+	// } catch (IllegalAccessException e) {
+	// throw new AccessException("Unable to access field: " + name, e);
+	// }
+	// }
+	// Method m = ReflectionUtils.findGetterForProperty(propertyName, relevantClass);
+	// if (m != null) {
+	// try {
+	// if (!m.isAccessible())
+	// m.setAccessible(true);
+	// return m.invoke(target);
+	// } catch (IllegalArgumentException e) {
+	// throw new AccessException("Unable to access property '" + name + "' through getter", e);
+	// } catch (IllegalAccessException e) {
+	// throw new AccessException("Unable to access property '" + name + "' through getter", e);
+	// } catch (InvocationTargetException e) {
+	// throw new AccessException("Unable to access property '" + name + "' through getter", e);
+	// }
+	// }
+	// return null;
+	// }
+	// public void write(EvaluationContext context, Object target, Object name, Object newValue) throws AccessException
+	// {
+	// if (target==null) {
+	// return;
+	// }
+	// Class<?> relevantClass = (target instanceof Class ? (Class<?>) target : target.getClass());
+	// if (!(name instanceof String))
+	// return;
+	// Field field = ReflectionUtils.findField((String) name, relevantClass);
+	// if (field != null) {
+	// try {
+	// if (!field.isAccessible())
+	// field.setAccessible(true);
+	// field.set(target, newValue);
+	// } catch (IllegalArgumentException e) {
+	// throw new AccessException("Unable to write to property '" + name + "'", e);
+	// } catch (IllegalAccessException e) {
+	// throw new AccessException("Unable to write to property '" + name + "'", e);
+	// }
+	// }
+	// Method m = ReflectionUtils.findSetterForProperty((String) name, relevantClass);
+	// if (m != null) {
+	// try {
+	// if (!m.isAccessible())
+	// m.setAccessible(true);
+	// m.invoke(target, newValue);
+	// } catch (IllegalArgumentException e) {
+	// throw new AccessException("Unable to access property '" + name + "' through setter", e);
+	// } catch (IllegalAccessException e) {
+	// throw new AccessException("Unable to access property '" + name + "' through setter", e);
+	// } catch (InvocationTargetException e) {
+	// throw new AccessException("Unable to access property '" + name + "' through setter", e);
+	// }
+	// }
+	// }
+	//
+	//
+	// public boolean canWrite(EvaluationContext context, Object target, Object name) throws AccessException {
+	// if (target==null) {
+	// return false;
+	// }
+	// Class<?> relevantClass = (target instanceof Class ? (Class<?>) target : target.getClass());
+	// if (!(name instanceof String))
+	// return false;
+	// Field field = ReflectionUtils.findField((String) name, relevantClass);
+	// if (field != null)
+	// return true;
+	// Method m = ReflectionUtils.findSetterForProperty((String) name, relevantClass);
+	// if (m != null)
+	// return true;
+	// return false;
+	// }
+}

+ 58 - 0
spring-el/src/main/java/org/springframework/expression/spel/reflection/ReflectionPropertyWriterExecutor.java

@@ -0,0 +1,58 @@
+package org.springframework.expression.spel.reflection;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.springframework.expression.AccessException;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.PropertyWriterExecutor;
+
+public class ReflectionPropertyWriterExecutor implements PropertyWriterExecutor {
+
+	private Method methodToAccessProperty;
+	private Field fieldToAccessProperty;
+	private final String propertyName;
+
+	public ReflectionPropertyWriterExecutor(String propertyName, Method method) {
+		this.propertyName = propertyName;
+		methodToAccessProperty = method;
+	}
+
+	public ReflectionPropertyWriterExecutor(String propertyName, Field field) {
+		this.propertyName = propertyName;
+		fieldToAccessProperty = field;
+	}
+
+	// public Object execute(EvaluationContext context, Object target) throws AccessException {
+	public void execute(EvaluationContext evaluationContext, Object target, Object newValue) throws AccessException {
+		if (methodToAccessProperty != null) {
+			try {
+				if (!methodToAccessProperty.isAccessible())
+					methodToAccessProperty.setAccessible(true);
+				methodToAccessProperty.invoke(target, newValue);
+				return;
+			} catch (IllegalArgumentException e) {
+				throw new AccessException("Unable to access property '" + propertyName + "' through setter", e);
+			} catch (IllegalAccessException e) {
+				throw new AccessException("Unable to access property '" + propertyName + "' through setter", e);
+			} catch (InvocationTargetException e) {
+				throw new AccessException("Unable to access property '" + propertyName + "' through setter", e);
+			}
+		}
+		if (fieldToAccessProperty != null) {
+			try {
+				if (!fieldToAccessProperty.isAccessible()) {
+					fieldToAccessProperty.setAccessible(true);
+				}
+				fieldToAccessProperty.set(target, newValue);
+				return;
+			} catch (IllegalArgumentException e) {
+				throw new AccessException("Unable to access field: " + propertyName, e);
+			} catch (IllegalAccessException e) {
+				throw new AccessException("Unable to access field: " + propertyName, e);
+			}
+		}
+		throw new AccessException("No method or field accessor found for property '" + propertyName + "'");
+	}
+}

+ 562 - 0
spring-el/src/main/java/org/springframework/expression/spel/reflection/ReflectionUtils.java

@@ -0,0 +1,562 @@
+/*
+ * Copyright 2004-2008 the original author or authors.
+ *
+ * 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.springframework.expression.spel.reflection;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.expression.EvaluationException;
+import org.springframework.expression.TypeConverter;
+import org.springframework.expression.spel.SpelException;
+import org.springframework.expression.spel.SpelMessages;
+
+/**
+ * Utility methods used by the reflection resolver code to discover the correct methods/constructors and fields that
+ * should be used in expressions.
+ * 
+ * @author Andy Clement
+ */
+@SuppressWarnings("unchecked")
+public class ReflectionUtils {
+
+	/**
+	 * Locate a constructor on a type. There are three kinds of match that might occur:
+	 * <ol>
+	 * <li>An exact match where the types of the arguments match the types of the constructor
+	 * <li>An in-exact match where the types we are looking for are subtypes of those defined on the constructor
+	 * <li>A match where we are able to convert the arguments into those expected by the constructor, according to the
+	 * registered type converter.
+	 * </ol>
+	 * 
+	 * @param typeConverter a converter that can be used to determine if the supplied arguments can be converted to
+	 * expected arguments
+	 * @param type the type being searched for a valid constructor
+	 * @param argumentTypes the types of the arguments we want the constructor to have
+	 * @return a DiscoveredConstructor object or null if non found
+	 * @throws SpelException
+	 */
+	public static DiscoveredMethod findMethod(TypeConverter typeConverter, String name, Class<?>[] argumentTypes,
+			Class<?> type, boolean conversionAllowed) throws SpelException {
+		Method[] methods = type.getMethods();
+		Method closeMatch = null;
+		Integer[] argsToConvert = null;
+		boolean multipleOptions = false;
+		Method matchRequiringConversion = null;
+		for (int i = 0; i < methods.length; i++) {
+			Method method = methods[i];
+			if (method.isBridge()) {
+				continue;
+			}
+			if (method.getName().equals(name)) {
+				ArgumentsMatchInfo matchInfo = null;
+				if (method.isVarArgs() && argumentTypes.length >= (method.getParameterTypes().length - 1)) {
+					// *sigh* complicated
+					matchInfo = compareArgumentsVarargs(method.getParameterTypes(), argumentTypes, typeConverter,
+							conversionAllowed);
+				} else if (method.getParameterTypes().length == argumentTypes.length) {
+					// name and parameter number match, check the arguments
+					matchInfo = compareArguments(method.getParameterTypes(), argumentTypes, typeConverter,
+							conversionAllowed);
+				}
+				if (matchInfo != null) {
+					if (matchInfo.kind == ArgsMatchKind.EXACT) {
+						return new DiscoveredMethod(method, null);
+					} else if (matchInfo.kind == ArgsMatchKind.CLOSE) {
+						closeMatch = method;
+					} else if (matchInfo.kind == ArgsMatchKind.REQUIRES_CONVERSION) {
+						if (matchRequiringConversion != null) {
+							multipleOptions = true;
+						}
+						argsToConvert = matchInfo.argsRequiringConversion;
+						matchRequiringConversion = method;
+					}
+				}
+			}
+		}
+		if (closeMatch != null) {
+			return new DiscoveredMethod(closeMatch, null);
+		} else if (matchRequiringConversion != null) {
+			if (multipleOptions) {
+				throw new SpelException(SpelMessages.MULTIPLE_POSSIBLE_METHODS, name);
+			}
+			return new DiscoveredMethod(matchRequiringConversion, argsToConvert);
+		} else {
+			return null;
+		}
+	}
+
+	/**
+	 * Locate a constructor on the type. There are three kinds of match that might occur:
+	 * <ol>
+	 * <li>An exact match where the types of the arguments match the types of the constructor
+	 * <li>An in-exact match where the types we are looking for are subtypes of those defined on the constructor
+	 * <li>A match where we are able to convert the arguments into those expected by the constructor, according to the
+	 * registered type converter.
+	 * </ol>
+	 * 
+	 * @param typeConverter a converter that can be used to determine if the supplied arguments can be converted to
+	 * expected arguments
+	 * @param type the type being searched for a valid constructor
+	 * @param argumentTypes the types of the arguments we want the constructor to have
+	 * @return a DiscoveredConstructor object or null if non found
+	 */
+	public static DiscoveredConstructor findConstructor(TypeConverter typeConverter, Class<?> type,
+			Class<?>[] argumentTypes, boolean conversionAllowed) {
+		Constructor[] ctors = type.getConstructors();
+		Constructor closeMatch = null;
+		Integer[] argsToConvert = null;
+		Constructor matchRequiringConversion = null;
+		for (int i = 0; i < ctors.length; i++) {
+			Constructor ctor = ctors[i];
+			if (ctor.isVarArgs() && argumentTypes.length >= (ctor.getParameterTypes().length - 1)) {
+				// *sigh* complicated
+				// Basically.. we have to have all parameters match up until the varargs one, then the rest of what is
+				// being provided should be
+				// the same type whilst the final argument to the method must be an array of that (oh, how easy...not) -
+				// or the final parameter
+				// we are supplied does match exactly (it is an array already).
+				ArgumentsMatchInfo matchInfo = compareArgumentsVarargs(ctor.getParameterTypes(), argumentTypes,
+						typeConverter, conversionAllowed);
+				if (matchInfo != null) {
+					if (matchInfo.kind == ArgsMatchKind.EXACT) {
+						return new DiscoveredConstructor(ctor, null);
+					} else if (matchInfo.kind == ArgsMatchKind.CLOSE) {
+						closeMatch = ctor;
+					} else if (matchInfo.kind == ArgsMatchKind.REQUIRES_CONVERSION) {
+						argsToConvert = matchInfo.argsRequiringConversion;
+						matchRequiringConversion = ctor;
+					}
+				}
+
+			} else if (ctor.getParameterTypes().length == argumentTypes.length) {
+				// worth a closer look
+				ArgumentsMatchInfo matchInfo = compareArguments(ctor.getParameterTypes(), argumentTypes, typeConverter,
+						conversionAllowed);
+				if (matchInfo != null) {
+					if (matchInfo.kind == ArgsMatchKind.EXACT) {
+						return new DiscoveredConstructor(ctor, null);
+					} else if (matchInfo.kind == ArgsMatchKind.CLOSE) {
+						closeMatch = ctor;
+					} else if (matchInfo.kind == ArgsMatchKind.REQUIRES_CONVERSION) {
+						argsToConvert = matchInfo.argsRequiringConversion;
+						matchRequiringConversion = ctor;
+					}
+				}
+			}
+		}
+		if (closeMatch != null) {
+			return new DiscoveredConstructor(closeMatch, null);
+		} else if (matchRequiringConversion != null) {
+			return new DiscoveredConstructor(matchRequiringConversion, argsToConvert);
+		} else {
+			return null;
+		}
+	}
+
+	/**
+	 * Compare argument arrays and return information about whether they match. A supplied type converter and
+	 * conversionAllowed flag allow for matches to take into account that a type may be transformed into a different
+	 * type by the converter.
+	 * 
+	 * @param expectedArgTypes the array of types the method/constructor is expecting
+	 * @param suppliedArgTypes the array of types that are being supplied at the point of invocation
+	 * @param typeConverter a registered type converter
+	 * @param conversionAllowed if true then allow for what the type converter can do when seeing if a supplied type can
+	 * match an expected type
+	 * @return a MatchInfo object indicating what kind of match it was or null if it was not a match
+	 */
+	private static ArgumentsMatchInfo compareArguments(Class[] expectedArgTypes, Class[] suppliedArgTypes,
+			TypeConverter typeConverter, boolean conversionAllowed) {
+		ArgsMatchKind match = ArgsMatchKind.EXACT;
+		List<Integer> argsRequiringConversion = null;
+		for (int i = 0; i < expectedArgTypes.length && match != null; i++) {
+			Class suppliedArg = suppliedArgTypes[i];
+			Class expectedArg = expectedArgTypes[i];
+			if (expectedArg != suppliedArg) {
+				if (expectedArg.isAssignableFrom(suppliedArg) || areBoxingCompatible(expectedArg, suppliedArg)
+				/* || isWidenableTo(expectedArg, suppliedArg) */) {
+					if (match != ArgsMatchKind.REQUIRES_CONVERSION) {
+						match = ArgsMatchKind.CLOSE;
+					}
+				} else if (typeConverter.canConvert(suppliedArg, expectedArg)) {
+					if (argsRequiringConversion == null) {
+						argsRequiringConversion = new ArrayList<Integer>();
+					}
+					argsRequiringConversion.add(i);
+					match = ArgsMatchKind.REQUIRES_CONVERSION;
+				} else {
+					match = null;
+				}
+			}
+		}
+		if (match == null) {
+			return null;
+		} else {
+			if (match == ArgsMatchKind.REQUIRES_CONVERSION) {
+				return new ArgumentsMatchInfo(match, argsRequiringConversion.toArray(new Integer[] {}));
+			} else {
+				return new ArgumentsMatchInfo(match);
+			}
+		}
+	}
+
+	/**
+	 * Compare argument arrays and return information about whether they match. A supplied type converter and
+	 * conversionAllowed flag allow for matches to take into account that a type may be transformed into a different
+	 * type by the converter. This variant of compareArguments allows for a varargs match.
+	 * 
+	 * @param expectedArgTypes the array of types the method/constructor is expecting
+	 * @param suppliedArgTypes the array of types that are being supplied at the point of invocation
+	 * @param typeConverter a registered type converter
+	 * @param conversionAllowed if true then allow for what the type converter can do when seeing if a supplied type can
+	 * match an expected type
+	 * @return a MatchInfo object indicating what kind of match it was or null if it was not a match
+	 */
+	private static ArgumentsMatchInfo compareArgumentsVarargs(Class[] expectedArgTypes, Class[] suppliedArgTypes,
+			TypeConverter typeConverter, boolean conversionAllowed) {
+		ArgsMatchKind match = ArgsMatchKind.EXACT;
+		List<Integer> argsRequiringConversion = null;
+
+		// Check up until the varargs argument:
+
+		// Deal with the arguments up to 'expected number' - 1
+		for (int i = 0; i < expectedArgTypes.length - 1 && match != null; i++) {
+			Class suppliedArg = suppliedArgTypes[i];
+			Class expectedArg = expectedArgTypes[i];
+			if (expectedArg != suppliedArg) {
+				if (expectedArg.isAssignableFrom(suppliedArg) || areBoxingCompatible(expectedArg, suppliedArg)
+				/* || isWidenableTo(expectedArg, suppliedArg) */) {
+					if (match != ArgsMatchKind.REQUIRES_CONVERSION) {
+						match = ArgsMatchKind.CLOSE;
+					}
+				} else if (typeConverter.canConvert(suppliedArg, expectedArg)) {
+					if (argsRequiringConversion == null) {
+						argsRequiringConversion = new ArrayList<Integer>();
+					}
+					argsRequiringConversion.add(i);
+					match = ArgsMatchKind.REQUIRES_CONVERSION;
+				} else {
+					match = null;
+				}
+			}
+		}
+		// Already does not match
+		if (match == null) {
+			return null;
+		}
+
+		// Special case: there is one parameter left and it is an array and it matches the varargs expected argument -
+		// that is a match, the caller has already built the array
+		if (suppliedArgTypes.length == expectedArgTypes.length
+				&& expectedArgTypes[expectedArgTypes.length - 1] == suppliedArgTypes[suppliedArgTypes.length - 1]) {
+
+		} else {
+
+			// Now... we have the final argument in the method we are checking as a match and we have 0 or more other
+			// arguments left to pass to it.
+			Class varargsParameterType = expectedArgTypes[expectedArgTypes.length - 1].getComponentType();
+
+			// All remaining parameters must be of this type or convertable to this type
+			for (int i = expectedArgTypes.length - 1; i < suppliedArgTypes.length; i++) {
+				Class suppliedArg = suppliedArgTypes[i];
+				if (varargsParameterType != suppliedArg) {
+					if (varargsParameterType.isAssignableFrom(suppliedArg)
+							|| areBoxingCompatible(varargsParameterType, suppliedArg)
+					/* || isWidenableTo(expectedArg, suppliedArg) */) {
+						if (match != ArgsMatchKind.REQUIRES_CONVERSION) {
+							match = ArgsMatchKind.CLOSE;
+						}
+					} else if (typeConverter.canConvert(suppliedArg, varargsParameterType)) {
+						if (argsRequiringConversion == null) {
+							argsRequiringConversion = new ArrayList<Integer>();
+						}
+						argsRequiringConversion.add(i);
+						match = ArgsMatchKind.REQUIRES_CONVERSION;
+					} else {
+						match = null;
+					}
+				}
+			}
+		}
+
+		if (match == null) {
+			return null;
+		} else {
+			if (match == ArgsMatchKind.REQUIRES_CONVERSION) {
+				return new ArgumentsMatchInfo(match, argsRequiringConversion.toArray(new Integer[] {}));
+			} else {
+				return new ArgumentsMatchInfo(match);
+			}
+		}
+	}
+
+	// TODO optimize implementation of areBoxingCompatible
+	private static boolean areBoxingCompatible(Class class1, Class class2) {
+		if (class1 == Integer.class && class2 == Integer.TYPE)
+			return true;
+		if (class1 == Float.class && class2 == Float.TYPE)
+			return true;
+		if (class1 == Double.class && class2 == Double.TYPE)
+			return true;
+		if (class1 == Short.class && class2 == Short.TYPE)
+			return true;
+		if (class1 == Long.class && class2 == Long.TYPE)
+			return true;
+		if (class1 == Boolean.class && class2 == Boolean.TYPE)
+			return true;
+		if (class1 == Character.class && class2 == Character.TYPE)
+			return true;
+		if (class1 == Byte.class && class2 == Byte.TYPE)
+			return true;
+		if (class2 == Integer.class && class1 == Integer.TYPE)
+			return true;
+		if (class2 == Float.class && class1 == Float.TYPE)
+			return true;
+		if (class2 == Double.class && class1 == Double.TYPE)
+			return true;
+		if (class2 == Short.class && class1 == Short.TYPE)
+			return true;
+		if (class2 == Long.class && class1 == Long.TYPE)
+			return true;
+		if (class2 == Boolean.class && class1 == Boolean.TYPE)
+			return true;
+		if (class2 == Character.class && class1 == Character.TYPE)
+			return true;
+		if (class2 == Byte.class && class1 == Byte.TYPE)
+			return true;
+		return false;
+	}
+
+	/**
+	 * Find a field of a certain name on a specified class
+	 */
+	public final static Field findField(String name, Class<?> clazz, boolean mustBeStatic) {
+		Field[] fields = clazz.getFields(); // TODO use getDeclaredFields() and search up hierarchy?
+		for (int i = 0; i < fields.length; i++) {
+			Field field = fields[i];
+			if (field.getName().equals(name) && (mustBeStatic ? Modifier.isStatic(field.getModifiers()) : true)) {
+				return field;
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * Find a getter method for the specified property. A getter is defined as a method whose name start with the prefix
+	 * 'get' and the rest of the name is the same as the property name (with the first character uppercased).
+	 */
+	public static Method findGetterForProperty(String propertyName, Class<?> clazz, boolean mustBeStatic) {
+		Method[] ms = clazz.getMethods();// TODO use getDeclaredMethods() and search up hierarchy?
+		StringBuilder sb = new StringBuilder();
+		sb.append("get").append(propertyName.substring(0, 1).toUpperCase()).append(propertyName.substring(1));
+		String expectedGetterName = sb.toString();
+		for (int i = 0; i < ms.length; i++) {
+			Method method = ms[i];
+			if (method.getParameterTypes().length == 0
+					&& (mustBeStatic ? Modifier.isStatic(method.getModifiers()) : true)
+					&& method.getName().equals(expectedGetterName)) {
+				return method;
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * Find a setter method for the specified property
+	 */
+	public static Method findSetterForProperty(String propertyName, Class<?> clazz, boolean mustBeStatic) {
+		Method[] ms = clazz.getMethods(); // TODO use getDeclaredMethods() and search up hierarchy?
+		StringBuilder sb = new StringBuilder();
+		sb.append("set").append(propertyName.substring(0, 1).toUpperCase()).append(propertyName.substring(1));
+		String setterName = sb.toString();
+		for (int i = 0; i < ms.length; i++) {
+			Method method = ms[i];
+			if (method.getParameterTypes().length == 1
+					&& (mustBeStatic ? Modifier.isStatic(method.getModifiers()) : true)
+					&& method.getName().equals(setterName)) {
+				return method;
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * An instance of MatchInfo describes what kind of match was achieved between two sets of arguments - the set that a
+	 * method/constructor is expecting and the set that are being supplied at the point of invocation. If the kind
+	 * indicates that conversion is required for some of the arguments then the arguments that require conversion are
+	 * listed in the argsRequiringConversion array.
+	 * 
+	 */
+	private static class ArgumentsMatchInfo {
+		ArgsMatchKind kind;
+		Integer[] argsRequiringConversion;
+
+		ArgumentsMatchInfo(ArgsMatchKind kind, Integer[] integers) {
+			this.kind = kind;
+			argsRequiringConversion = integers;
+		}
+
+		ArgumentsMatchInfo(ArgsMatchKind kind) {
+			this.kind = kind;
+		}
+	}
+
+	private static enum ArgsMatchKind {
+		EXACT, CLOSE, REQUIRES_CONVERSION;
+	}
+
+	/**
+	 * When a match is found searching for a particular constructor, this object captures the constructor object and
+	 * details of which arguments require conversion for the call to be allowed.
+	 */
+	public static class DiscoveredConstructor {
+		public Constructor theConstructor;
+		public Integer[] argumentsRequiringConversion;
+
+		public DiscoveredConstructor(Constructor theConstructor, Integer[] argsToConvert) {
+			this.theConstructor = theConstructor;
+			argumentsRequiringConversion = argsToConvert;
+		}
+	}
+
+	/**
+	 * When a match is found searching for a particular method, this object captures the method object and details of
+	 * which arguments require conversion for the call to be allowed.
+	 */
+	public static class DiscoveredMethod {
+		public Method theMethod;
+		public Integer[] argumentsRequiringConversion;
+
+		public DiscoveredMethod(Method theMethod, Integer[] argsToConvert) {
+			this.theMethod = theMethod;
+			argumentsRequiringConversion = argsToConvert;
+		}
+	}
+
+	static void convertArguments(Class[] parameterTypes, boolean isVarargs, TypeConverter converter,
+			Integer[] argsRequiringConversion, Object... arguments) throws EvaluationException {
+		Class varargsType = null;
+		if (isVarargs) {
+			varargsType = parameterTypes[parameterTypes.length - 1].getComponentType();
+		}
+		for (int i = 0; i < argsRequiringConversion.length; i++) {
+			int argPosition = argsRequiringConversion[i];
+			Class targetType = null;
+			if (isVarargs && argPosition >= (parameterTypes.length - 1)) {
+				targetType = varargsType;
+			} else {
+				targetType = parameterTypes[argPosition];
+			}
+			// try {
+			arguments[argPosition] = converter.convertValue(arguments[argPosition], targetType);
+			// } catch (EvaluationException e) {
+			// throw new SpelException(e, SpelMessages.PROBLEM_DURING_TYPE_CONVERSION, "Converter failed to convert '"
+			// + arguments[argPosition] + " to type '" + targetType + "'");
+			// }
+		}
+	}
+
+	public static void convertArguments(Class[] parameterTypes, boolean isVarargs, TypeConverter converter,
+			Object... arguments) throws EvaluationException {
+		Class varargsType = null;
+		if (isVarargs) {
+			varargsType = parameterTypes[parameterTypes.length - 1].getComponentType();
+		}
+		for (int i = 0; i < arguments.length; i++) {
+			Class targetType = null;
+			if (isVarargs && i >= (parameterTypes.length - 1)) {
+				targetType = varargsType;
+			} else {
+				targetType = parameterTypes[i];
+			}
+			if (converter == null) {
+				throw new SpelException(SpelMessages.PROBLEM_DURING_TYPE_CONVERSION,
+						"No converter available to convert '" + arguments[i] + " to type '" + targetType + "'");
+			}
+			try {
+				if (arguments[i] != null && arguments[i].getClass() != targetType) {
+					arguments[i] = converter.convertValue(arguments[i], targetType);
+				}
+			} catch (EvaluationException e) {
+				// allows for another type converter throwing a different kind of EvaluationException
+				if (!(e instanceof SpelException)) {
+					throw new SpelException(e, SpelMessages.PROBLEM_DURING_TYPE_CONVERSION,
+							"Converter failed to convert '" + arguments[i].getClass().getName() + "' to type '"
+									+ targetType + "'");
+				}
+				throw e;
+			}
+		}
+	}
+
+	/**
+	 * Package up the arguments so that they correctly match what is expected in parameterTypes. For example, if
+	 * parameterTypes is (int, String[]) because the second parameter was declared String... then if arguments is
+	 * [1,"a","b"] then it must be repackaged as [1,new String[]{"a","b"}] in order to match the expected
+	 * parameterTypes.
+	 * 
+	 * @param parameterTypes the types of the parameters for the invocation
+	 * @param arguments the arguments to be setup ready for the invocation
+	 * @return a repackaged array of arguments where any varargs setup has been done
+	 */
+	public static Object[] setupArgumentsForVarargsInvocation(Class[] parameterTypes, Object... arguments) {
+		// Check if array already built for final argument
+		int nParams = parameterTypes.length;
+		int nArgs = arguments.length;
+
+		// Check if repackaging is needed:
+		if (nParams != arguments.length
+				|| parameterTypes[nParams - 1] != (arguments[nArgs - 1] == null ? null : arguments[nArgs - 1]
+						.getClass())) {
+			int arraySize = 0; // zero size array if nothing to pass as the varargs parameter
+			if (arguments != null && nArgs >= nParams) {
+				arraySize = nArgs - (nParams - 1);
+			}
+			Object[] repackagedArguments = (Object[]) Array.newInstance(parameterTypes[nParams - 1].getComponentType(),
+					arraySize);
+
+			// Copy all but the varargs arguments
+			for (int i = 0; i < arraySize; i++) {
+				repackagedArguments[i] = arguments[nParams + i - 1];
+			}
+			// Create an array for the varargs arguments
+			Object[] newArgs = new Object[nParams];
+			for (int i = 0; i < newArgs.length - 1; i++) {
+				newArgs[i] = arguments[i];
+			}
+			newArgs[newArgs.length - 1] = repackagedArguments;
+			return newArgs;
+		}
+		return arguments;
+	}
+
+	public static Object[] prepareArguments(TypeConverter converter, Method m, Object[] arguments)
+			throws EvaluationException {
+		if (arguments != null) {
+			ReflectionUtils.convertArguments(m.getParameterTypes(), m.isVarArgs(), converter, arguments);
+		}
+		if (m.isVarArgs()) {
+			arguments = ReflectionUtils.setupArgumentsForVarargsInvocation(m.getParameterTypes(), arguments);
+		}
+		return arguments;
+	}
+
+}

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác