浏览代码

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

Luke Taylor 17 年之前
父节点
当前提交
0dd82cb91a
共有 100 个文件被更改,包括 15053 次插入0 次删除
  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;
+	}
+
+}

部分文件因为文件数量过多而无法显示