Move tag from exec to jexl where it belongs

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/jexl/tags/COMMONS_JEXL_2_0@1351799 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/COMMONS_JEXL_2_0/LICENSE.txt b/COMMONS_JEXL_2_0/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/COMMONS_JEXL_2_0/LICENSE.txt
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   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.
diff --git a/COMMONS_JEXL_2_0/NOTICE.txt b/COMMONS_JEXL_2_0/NOTICE.txt
new file mode 100644
index 0000000..83b2c85
--- /dev/null
+++ b/COMMONS_JEXL_2_0/NOTICE.txt
@@ -0,0 +1,5 @@
+Apache Commons JEXL
+Copyright 2001-2010 The Apache Software Foundation
+
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
diff --git a/COMMONS_JEXL_2_0/PROPOSAL.html b/COMMONS_JEXL_2_0/PROPOSAL.html
new file mode 100644
index 0000000..0f7ba1a
--- /dev/null
+++ b/COMMONS_JEXL_2_0/PROPOSAL.html
@@ -0,0 +1,90 @@
+<html>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You 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.
+-->
+<head>
+<title>Proposal for Jexl Package</title>
+</head>
+<body bgcolor="white">
+
+<div align="center">
+<h1>Proposal for <em>Jexl</em> Package</h1>
+</div>
+
+<h3>(0) Rationale</h3>
+
+<p>The <em>Jexl</em> package implements a simple expression language for
+accessing Java objects.
+</p>
+
+<h3>(1) Scope of the Package</h3>
+<p>
+   The package will create and maintain a number of classes for
+   implementing a simple expression language and processing engine,
+   and to be distributed under the ASF license.
+</p>
+
+<h3>(1.5) Interaction With Other Packages</h3>
+
+<p><em>Jexl</em> relies  on standard JDK 1.2 (or later) APIs for
+production deployment. </p>
+
+<p>
+<i>Jexl</i> utilizes the JUnit unit testing framework for developing and
+executing unit tests, but this is of interest only to developers of the
+component.
+</p>
+
+<p>
+<i>Jexl</i> also depends on Jakarta Velocity, Commons Logging, dom4j
+and Velocity DVSL for documentation rendering.
+</p>
+
+<h3>(2) Initial Source of the Package</h3>
+
+<p>
+   The code base is new and uses ideas from Jakarta Velocity.
+</p>
+
+<p>The proposed package name for the new component is
+<code>org.apache.commons.jexl</code>.</p>
+
+
+<h3>(3)  Required Jakarta-Commons Resources</h3>
+
+<ul>
+<li>CVS Repository - New directory <code>jexl</code> in the
+    <code>jakarta-commons</code> CVS repository.  All initial committers
+    are already committers on <code>jakarta-commons</code>, so no
+    additional user setups are required.</li>
+<li>Mailing List - Discussions will take place on the general
+    <em>jakarta-commons@jakarta.apache.org</em> mailing list.  To help
+    list subscribers identify messages of interest, it is suggested that
+    the message subject of messages about this component be prefixed with
+    [Jexl].  Strongly suggested.</li>
+<li>Bugzilla - New component "Jexl" under the "Commons" product
+    category, with appropriate version identifiers as needed.</li>
+<li>Jyve FAQ - New category "commons-jexl" (when available). </li>
+</ul>
+
+
+<h3>(4) Initial Committers</h3>
+<ul>
+   <li>Geir Magnusson Jr.</li>
+   <li>James Strachan</li>
+</ul>
+</body>
+</html>
diff --git a/COMMONS_JEXL_2_0/RELEASE-NOTES.txt b/COMMONS_JEXL_2_0/RELEASE-NOTES.txt
new file mode 100644
index 0000000..1462d23
--- /dev/null
+++ b/COMMONS_JEXL_2_0/RELEASE-NOTES.txt
@@ -0,0 +1,176 @@
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+$Id$
+
+                            Commons JEXL Package
+                                Version 2.0
+                               Release Notes
+
+
+INTRODUCTION:
+=============
+
+JEXL is an Expression Language supporting most of the constructs in the
+JSTL Expression Language, along with some additional extensions.
+
+  http://commons.apache.org/jexl/
+
+Changes in this version include:
+
+Incompatible Changes
+====================
+
+Now requires Java 1.5 or later.
+
+Version 2.0 resides in the org.apache.commons.jexl2 package; part of the version 1.x API is reimplemented as an
+add-on source library in the jexl-compat directory; since it can not fully reimplement the original public 1.x, it may
+only be used to ease transition in strictly controlled deployments.
+
+The following classes are implemented through the jexl-compat source library:
+  * ExpressionFactory
+  * ScriptFactory
+  * Expression
+  * Script
+  * JexlContext
+  * JexlHelper
+
+Migration notes
+===============
+
+When migrating from jexl 1.x to jexl 2.0, the following hints may be helpfull.
+
+The following classes no longer exist:
+  * ExpressionFactory, ScriptFactory: create a JexlEngine and use createExpression() or createScript()
+  instead.
+
+The following classes have been renamed and replaced:
+
+  * VelMethod <=> JexlMethod
+  * VelPropertyGet <=> JexlPropertyGet
+  * VelPropertySet <=> JexlPropertySet
+
+The following methods have been removed:
+
+  * Info.getTemplateName() - use Info.getName() instead
+  * Expression.addPostResolver() / Expression.addPreResolver() - set ant-like variables in JexlContext, implement
+    a specific JexlContext or derive JexlcontextInterpreter/JexlEngine instead
+
+Behavior changes
+================
+
+* Public fields are considered when using JexlPropertyGet / JexlPropertySet: Jexl 1.x behavior can be reimplemented
+by subclassing UberspectImpl.
+
+*Division (/ operator) behavior change: division between integers no longer casts its operands to double; integer division
+    allways results in a integer. The 1.x behavior can be reimplemented by subclassing JexlArithmetic.
+
+New Features:
+=============
+
+Assignment expression: a = b (and a.b.c = d)
+   * Assigns a variable (ant-like variable or bean-property)
+
+Ternary operator expression: a ? b : c  (and a ?: c)
+   * The usual inline conditional shortcut and its 'Elvis' form (a ?: b evaluates as a ? a : b)
+
+Constructor call expression: new('my.class.name', arguments...)
+   * Creates a new instance of a class using the most appropriate constructor according
+     to the actual arguments
+
+Function namespace: ns:func(arguments...)
+   * A function namespace allows the use of class or instance methods in function calls
+
+UnifiedJEXL
+   * Adds ${...} and #{...} JSP/EL syntax support on top of the JexlEngine
+
+JSR-223 support
+   * Implement JSR-223 Scripting Engine for JEXL script (need BSF-3.0 on Java < 6)
+
+Error and exception handling
+   * Configuring the leniency and verbosity of the Jexl engine allows user control over the
+     error handling policy
+
+Bugs fixed:
+===========
+
+* JEXL-90:	Jexl parser allows invalid expressions, e.g. "a=1 b=2 3"
+* JEXL-88:	MethodKey.java - name clash getMostSpecific() with Java 1.5.0
+* JEXL-87:	Inconsistent behaviour of arithmetical operations
+* JEXL-81:	Introspector does not use ListGetExecutor for List
+* JEXL-80:	Lenient mode should not throw exception when {g,s}etting an undefined property
+* JEXL-78:	Ternary operator throws Exception when JexlEngine in strict mode
+* JEXL-76:	Remove unnecessary class VisitorAdapter
+* JEXL-71:	Parsing errors?
+* JEXL-67:	Potential NPE in util.introspection.MethodKey
+* JEXL-66:	testDottedNames expects map enumeration order
+* JEXL-64:	Inconsistent behaviour of dotted names
+* JEXL-62:	NPE in Interpreter
+* JEXL-59:	ClassMap holds a reference to class
+* JEXL-56:	Logging wrongly uses java.util.logging
+* JEXL-50:	Div operator does not do integer division
+* JEXL-49:	Block statements aren't parsed
+* JEXL-48:	NPE during expression evaluation
+* JEXL-45:	Unhandled division by zero
+* JEXL-42:	NullPointerException evaluating an expression
+* JEXL-40:	JEXL fails to find abstract public methods in the base class if overridden by non-public derived types
+* JEXL-32:	BigDecimal values are treated as Long values which results in loss of precision
+* JEXL-30:	ASTAddNode does not add BigDecimal objects correctly
+* JEXL-27:	Cannot assign a value to the property of an object, such as object.prop = value.
+* JEXL-26:	ASTArrayAccess messes up on fallback to JexlContext
+* JEXL-19:	Ternary conditional not supported
+* JEXL-3 :	Static method resolution and changes to context
+
+Other issues fixed (Improvements/New Features):
+===============================================
+
+* JEXL-95:	Enhance JSR-223 implementation
+* JEXL-94:	Allow stateful namespaces (ns:function)
+* JEXL-93:	Add public fields as targets of set/get property
+* JEXL-92:	JexlContext API should be more flexible
+* JEXL-89:	Drop main() and suite() methods from Test cases
+* JEXL-85:	2.0 grammar finishing touches & debugger update
+* JEXL-82:	Change foreach syntax
+* JEXL-77:	Rename last Velocity originated classes
+* JEXL-72:	Remove deprecated classes and methods entirely
+* JEXL-70:	Add main class to allow scripts etc to be tested
+* JEXL-63:	JSR-223 support
+* JEXL-61:	Usage of strong references on Method/Constructor & WeakHashMap usage
+* JEXL-60:	Refactor o.a.c.jexl.util and o.a.c.jexl.util.introspection
+* JEXL-58:	UnifiedJEXL
+* JEXL-57:	Change pom.xml to make it Netbeans Maven2 plugin friendly
+* JEXL-55:	JEXL 2.0 redux, attempting to restart the effort to release 2.0
+* JEXL-54:	Light performance enhancements
+* JEXL-47:	Allow single-line comments with //
+* JEXL-43:	Website overview does not mention method calls and new 2.0 features
+* JEXL-41:	Allow nested ${} evaluation
+* JEXL-35:	Final API requirements
+* JEXL-34:	Remove pre and post resolution of variables via the JexlExprResolver classes.
+* JEXL-33:	Remove unnecessary throws Exception from various classes
+* JEXL-29:	Support non-object-level functions/methods, as size and empty function
+* JEXL-25:	Call method with varargs
+* JEXL-24:	Support Long for integer literal instead of Integers
+* JEXL-21:	operator overloading / hooks on operator processing
+* JEXL-16:	allowing quote escaping
+* JEXL-15:	Needs definable functions
+* JEXL-11:	Don't make null convertible into anything
+* JEXL-10:	Make possible checking for unresolved variables
+
+Other Changes:
+==============
+
+o Add @since 2.0 tags to code so we can track API additions via Javadoc
+
diff --git a/COMMONS_JEXL_2_0/STATUS.html b/COMMONS_JEXL_2_0/STATUS.html
new file mode 100644
index 0000000..637dd3f
--- /dev/null
+++ b/COMMONS_JEXL_2_0/STATUS.html
@@ -0,0 +1,115 @@
+<html>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You 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.
+-->
+<head>
+<title>Status File for Apache Commons "Jexl" Package</title>
+</head>
+<body bgcolor="white">
+
+
+<div align="center">
+<h1>The Apache Commons <em>Jexl</em> Package</h1>
+$Id$<br/>
+<a href="#Introduction">[Introduction]</a>
+<a href="#Dependencies">[Dependencies]</a>
+<a href="#Release Info">[Release Info]</a>
+<a href="#Committers">[Committers]</a>
+<a href="#Action Items">[Action Items]</a>
+<br/><br/>
+</div>
+
+
+<a name="Introduction"></a>
+<h3>1.  INTRODUCTION</h3>
+
+<p>
+The <em>Jexl</em> package implements a simple expression language
+engine. It borrows many ideas from the JSTL Expression Language. Liberally.
+Ok, it's an attempt at an idependent implementation of the JSTL EL with
+extensions.
+</p>
+
+<a name="Dependencies"></a>
+<h3>2.  DEPENDENCIES</h3>
+
+<p>The <em>Jexl</em> package is dependent upon the following external
+components for development and use:</p>
+<ul>
+<li>
+    <a href="http://java.sun.com/j2se">Java Development Kit</a>
+    (Version 1.5 or later)
+</li>
+<li>
+  <a href="http://commons.apache.org/logging/">Apache Commons Logging</a>
+    (Version 1.0.1 or later)
+</li>
+<li>
+  <a href="http://jakarta.apache.org/velocity/">Jakarta Velocity DVSL</a>
+    (any version) - for documentation generation
+</li>
+<li>
+  <a href="http://www.dom4j.org/">dom4j</a>
+    (latest version) - for documentation generation
+</li>
+<li>
+<a href="http://www.junit.org">JUnit Testing Framework</a>
+    (Version 3.8.2 or later) - for unit tests only, not required
+    for deployment
+</li>
+</ul>
+
+<a name="Release Info"></a>
+<h3>3.  RELEASE INFO</h3>
+
+<p>Current Release: 1.1</p>
+
+<p>Planned Next Release: 2.0</p>
+
+<a name="Committers"></a>
+<h3>4.  COMMITTERS</h3>
+
+<p>The following individuals are the primary developers and maintainers of this
+component.  Developers who plan to use <em>Jexl</em> in their own
+projects are encouraged to collaborate on the future development of this
+component to ensure that it continues to meet a variety of needs.</p>
+<ul>
+   <li><a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a></li>
+   <li><a href="mailto:jstrachan@apache.org">James Strachan</a></li>
+   <li><a href="mailto:rdonkin@apache.org">Robert Burrell Donkin</a></li>
+   <li><a href="mailto:proyal@apache.org">Peter Royal</a></li>
+   <li><a href="mailto:henrib@apache.org">Henri Biestro</a></li>
+</ul>
+
+<a name="Action Items"></a><h3>5.  ACTION ITEMS</h3>
+
+<p>Want to help?  Here's some "to do" items the team has identified.</p>
+
+<table border="1">
+  <tr>
+    <th width="80%">Action Item</th>
+    <th width="20%">Volunteer</th>
+  </tr>
+
+  <tr>
+    <td>Please read the TODO.txt document in CVS</td>
+    <td align="center">&nbsp;</td>
+  </tr>
+
+  </table>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/doap_jexl.rdf b/COMMONS_JEXL_2_0/doap_jexl.rdf
new file mode 100644
index 0000000..b15b263
--- /dev/null
+++ b/COMMONS_JEXL_2_0/doap_jexl.rdf
@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You 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.
+-->
+<rdf:RDF xmlns="http://usefulinc.com/ns/doap#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:asfext="http://projects.apache.org/ns/asfext#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:doap="http://usefulinc.com/ns/doap#" xml:lang="en">
+  <Project rdf:about="http://commons.apache.org/jexl/">
+    <name>Apache Commons JEXL</name>
+    <homepage rdf:resource="http://commons.apache.org/jexl/"/>
+    <programming-language>Java</programming-language>
+    <category rdf:resource="http://projects.apache.org/category/library"/>
+    <license rdf:resource="http://usefulinc.com/doap/licenses/asl20"/>
+    <bug-database rdf:resource="http://issues.apache.org/jira/browse/JEXL"/>
+    <download-page rdf:resource="http://jakarta.apache.org/site/downloads/downloads_commons-jexl.cgi"/>
+    <asfext:pmc rdf:resource="http://commons.apache.org/"/>
+    <shortdesc xml:lang="en">Commons JEXL Expression Language Engine</shortdesc>
+    <description xml:lang="en">Jexl is an implementation of the JSTL Expression Language with extensions.</description>
+    <repository>
+      <SVNRepository>
+        <browse rdf:resource="http://svn.apache.org/repos/asf/commons/proper/jexl/trunk"/>
+        <location rdf:resource="http://svn.apache.org/repos/asf/commons/proper/jexl"/>
+      </SVNRepository>
+    </repository>
+    <release>
+      <version>
+        <name>commons-jexl</name>
+        <created>2009-11-16</created>
+        <revision>2.0-RC1</revision>
+      </version>
+      <version>
+        <name>commons-jexl</name>
+        <created>2009-03-25</created>
+        <revision>1.1</revision>
+      </version>
+    </release>
+    <mailing-list rdf:resource="http://commons.apache.org/mail-lists.html"/>
+  </Project>
+</rdf:RDF>
diff --git a/COMMONS_JEXL_2_0/jexl2-compat/pom.xml b/COMMONS_JEXL_2_0/jexl2-compat/pom.xml
new file mode 100644
index 0000000..b217c2a
--- /dev/null
+++ b/COMMONS_JEXL_2_0/jexl2-compat/pom.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You 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.
+-->
+<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">
+    <parent>
+        <groupId>org.apache.commons</groupId>
+        <artifactId>commons-parent</artifactId>
+        <version>12</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.apache.commons</groupId>
+    <artifactId>commons-jexl-compat</artifactId>
+    <name>Commons JEXL (1.x compatibility)</name>
+    <version>2.0-SNAPSHOT</version>
+    <inceptionYear>2003</inceptionYear>
+    <description>Jexl is an implementation of the JSTL Expression Language with extensions.</description>
+    <url>http://commons.apache.org/jexl/</url>
+
+    <issueManagement>
+        <system>jira</system>
+        <url>http://issues.apache.org/jira/browse/JEXL</url>
+    </issueManagement>
+
+    <scm>
+        <connection>scm:svn:http://svn.apache.org/repos/asf/commons/proper/jexl/trunk</connection>
+        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/commons/proper/jexl/trunk</developerConnection>
+        <url>http://svn.apache.org/viewvc/commons/proper/jexl/trunk</url>
+    </scm>
+
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <includes>
+                        <include>**/*Test.java</include>
+                    </includes>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.5</source>
+                    <target>1.5</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>3.8.1</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-logging</groupId>
+            <artifactId>commons-logging</artifactId>
+            <version>1.1.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-jexl</artifactId>
+            <version>2.0-SNAPSHOT</version>
+        </dependency>
+    </dependencies>
+
+    <properties>
+        <maven.compile.source>1.5</maven.compile.source>
+        <maven.compile.target>1.5</maven.compile.target>
+        <commons.componentid>jexl-compat</commons.componentid>
+        <commons.release.version>2.0</commons.release.version>
+        <!-- The RC version used in the staging repository URL. -->
+        <commons.rc.version>RC3</commons.rc.version>
+        <commons.binary.suffix />
+        <commons.jira.id>JEXL</commons.jira.id>
+        <commons.jira.pid>12310479</commons.jira.pid>
+        <!-- Temp fix until parent POM is updated -->
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    </properties>
+</project>
+
diff --git a/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/Expression.java b/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/Expression.java
new file mode 100644
index 0000000..5aa20df
--- /dev/null
+++ b/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/Expression.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl;
+
+/**
+ * Jexl-1.x compatible expression.
+ * @since 2.0
+ * @version $Id$
+ */
+public interface Expression extends org.apache.commons.jexl2.Expression {
+    /**
+     * Evaluates the expression with the variables contained in the
+     * supplied {@link JexlContext}.
+     *
+     * @param context A JexlContext containing variables.
+     * @return The result of this evaluation
+     * @throws Exception on any error
+     */
+    Object evaluate(JexlContext context) throws Exception;
+}
diff --git a/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/ExpressionFactory.java b/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/ExpressionFactory.java
new file mode 100644
index 0000000..83b9db5
--- /dev/null
+++ b/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/ExpressionFactory.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl;
+
+import org.apache.commons.jexl2.JexlEngine;
+
+/**
+ * Creates Expression objects. 
+ * <p>
+ * To create a JEXL Expression object, pass
+ * valid JEXL syntax to the static createExpression() method:
+ * </p>
+ *
+ * <pre>
+ * String jexl = "array[1]";
+ * Expression expression = ExpressionFactory.createExpression( jexl );
+ * </pre>
+ *
+ * <p>
+ * When an {@link Expression} object is created, the JEXL syntax is
+ * parsed and verified.  If the supplied expression is neither an
+ * expression nor a reference, an exception is thrown from createException().
+ * </p>
+ * 
+ * <p>
+ * This is a convenience class; using an instance of a {@link JexlEngine}
+ * that serves the same purpose with more control is recommended.
+ * </p>
+ * @since 1.0
+ * @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
+ * @version $Id: ExpressionFactory.java 884175 2009-11-25 16:23:41Z henrib $
+ * @deprecated Create a JexlEngine and use the createScript method on that instead.
+ */
+@Deprecated
+public final class ExpressionFactory extends JexlOne {}
diff --git a/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/JexlContext.java b/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/JexlContext.java
new file mode 100644
index 0000000..dc22c41
--- /dev/null
+++ b/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/JexlContext.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl;
+
+import java.util.Map;
+
+/**
+ * Holds a Map of variables which are referenced in a JEXL expression.
+ *
+ *  @since 1.0
+ *  @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
+ *  @version $Id: JexlContext.java 480412 2006-11-29 05:11:23Z bayard $
+ */
+public interface JexlContext {
+    /**
+     * Replaces variables in a JexlContext with the variables contained
+     * in the supplied Map.  When setVars() is called on a JexlContext,
+     * it clears the current Map and puts each entry of the
+     * supplied Map into the current variable Map.
+     *
+     * @param vars Contents of vars will be replaced with the content
+     *      of this Map
+     */
+    void setVars(Map<String,Object> vars);
+
+    /**
+     * Retrives the Map of variables associated with this JexlContext.  The
+     * keys of this map correspond to variable names referenced in a
+     * JEXL expression.
+     *
+     * @return A reference to the variable Map associated with this JexlContext.
+     */
+    Map<String,Object> getVars();
+}
diff --git a/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/JexlHelper.java b/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/JexlHelper.java
new file mode 100644
index 0000000..c9d87ea
--- /dev/null
+++ b/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/JexlHelper.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl;
+
+import org.apache.commons.jexl.context.HashMapContext;
+
+/**
+ *  Helper to create a context.  In the current implementation of JEXL, there
+ *  is one implementation of JexlContext - {@link HashMapContext}, and there
+ *  is no reason not to directly instantiate {@link HashMapContext} in your
+ *  own application.
+ *
+ *  @since 1.0
+ *  @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
+ *  @version $Id: JexlHelper.java 480412 2006-11-29 05:11:23Z bayard $
+ */
+public class JexlHelper {
+    /** singleton instance. */
+    protected static JexlHelper helper = new JexlHelper();
+
+    /** @return the single instance. */
+    protected static JexlHelper getInstance() {
+        return helper;
+    }
+
+    /**
+     * Returns a new {@link JexlContext}.
+     * @return a new JexlContext
+     */
+    public static JexlContext createContext() {
+        return getInstance().newContext();
+    }
+
+    /**
+     * Creates and returns a new {@link JexlContext}.  
+     * The current implementation creates a new instance of 
+     * {@link HashMapContext}.
+     * @return a new JexlContext
+     */
+    protected JexlContext newContext() {
+        return new HashMapContext();
+    }
+}
diff --git a/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/JexlOne.java b/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/JexlOne.java
new file mode 100644
index 0000000..70bd01c
--- /dev/null
+++ b/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/JexlOne.java
@@ -0,0 +1,277 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl;
+
+import java.io.File;
+import java.net.URL;
+import org.apache.commons.jexl2.JexlEngine;
+import org.apache.commons.jexl2.Interpreter;
+import org.apache.commons.jexl2.JexlException;
+import org.apache.commons.jexl2.parser.JexlNode;
+import org.apache.commons.jexl2.parser.ASTJexlScript;
+
+/**
+ * This implements Jexl-1.x (Jelly) compatible behaviors on top of Jexl-2.0.
+ * @since 2.0
+ * @version $Id$
+ */
+public class JexlOne {
+    /**
+     * Default cache size.
+     */
+    private static final int CACHE_SIZE = 256;
+
+    /**
+     * Private constructor, ensure no instance.
+     */
+    protected JexlOne() {}
+
+
+    /**
+     * Lazy JexlEngine singleton through on demand holder idiom.
+     */
+    private static final class EngineHolder {
+        /** The shared instance. */
+        static final JexlOneEngine JEXL10 = new JexlOneEngine();
+        /**
+         * Non-instantiable.
+         */
+        private EngineHolder() {}
+    }
+
+
+    /**
+     * A Jexl1.x context wrapped into a Jexl2 context.
+     */
+    private static final class ContextAdapter implements org.apache.commons.jexl2.JexlContext {
+        /** The Jexl1.x context. */
+        private final JexlContext legacy;
+
+        /**
+         * Creates a jexl2.JexlContext from a jexl.JexlContext.
+         * @param ctxt10
+         */
+        ContextAdapter(JexlContext ctxt10) {
+            legacy = ctxt10;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Object get(String name) {
+            return legacy.getVars().get(name);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void set(String name, Object value) {
+            legacy.getVars().put(name, value);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean has(String name) {
+            return legacy.getVars().containsKey(name);
+        }
+
+        /**
+         * Adapts a Jexl-1.x context to a Jexl-2.0 context.
+         * @param aContext a oac.jexl context
+         * @return an oac.jexl2 context
+         */
+        static final org.apache.commons.jexl2.JexlContext adapt(JexlContext aContext) {
+            return aContext == null ? JexlOneEngine.EMPTY_CONTEXT : new ContextAdapter(aContext);
+        }
+    }
+
+
+    /**
+     * An interpreter made compatible with v1.1 behavior (at least Jelly's expectations).
+     */
+    private static final class JexlOneInterpreter extends Interpreter {
+        /**
+         * Creates an instance.
+         * @param jexl the jexl engine
+         * @param aContext the jexl context
+         */
+        public JexlOneInterpreter(JexlEngine jexl, JexlContext aContext) {
+            super(jexl, ContextAdapter.adapt(aContext));
+        }
+
+        /**{@inheritDoc}*/
+        @Override
+        public Object interpret(JexlNode node) {
+            try {
+                return node.jjtAccept(this, null);
+            } catch (JexlException xjexl) {
+                Throwable e = xjexl.getCause();
+                if (e instanceof RuntimeException) {
+                    throw (RuntimeException) e;
+                }
+                if (e instanceof IllegalStateException) {
+                    throw (IllegalStateException) e;
+                }
+                throw new IllegalStateException(e.getMessage(), e);
+            }
+        }
+
+        /**{@inheritDoc}*/
+        @Override
+        protected Object invocationFailed(JexlException xjexl) {
+            throw xjexl;
+        }
+
+        /**{@inheritDoc}*/
+        @Override
+        protected Object unknownVariable(JexlException xjexl) {
+            return null;
+        }
+    }
+
+    
+    /**
+     * An engine that uses a JexlOneInterpreter.
+     */
+    private static final class JexlOneEngine extends JexlEngine {
+        /**
+         * Default ctor, creates a cache and sets instance to verbose (ie non-silent).
+         */
+        private JexlOneEngine() {
+            super();
+            setCache(CACHE_SIZE);
+            setSilent(false);
+        }
+
+        /**{@inheritDoc}*/
+        protected Interpreter createInterpreter(JexlContext context) {
+            return new JexlOneInterpreter(this, context);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        protected Script createScript(ASTJexlScript tree, String text) {
+            return new JexlOneExpression(this, text, tree);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        protected Expression createExpression(ASTJexlScript tree, String text) {
+            return new JexlOneExpression(this, text, tree);
+        }
+    }
+
+    /**
+     * The specific Jexl-1.x expressions implementation.
+     */
+    private static final class JexlOneExpression
+            extends org.apache.commons.jexl2.ExpressionImpl
+            implements Expression, Script {
+        /**
+         * Default local ctor.
+         *
+         * @param engine the interpreter to evaluate the expression
+         * @param expr the expression.
+         * @param ref the parsed expression.
+         */
+        private JexlOneExpression(JexlOne.JexlOneEngine engine, String expr, ASTJexlScript ref) {
+            super(engine, expr, ref);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Object evaluate(JexlContext context) {
+            return super.evaluate(ContextAdapter.adapt(context));
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Object execute(JexlContext context) {
+            return super.execute(ContextAdapter.adapt(context));
+        }
+    }
+
+
+    /**
+     * Creates a Script from a String containing valid JEXL syntax.
+     * This method parses the script which validates the syntax.
+     *
+     * @param scriptText A String containing valid JEXL syntax
+     * @return A {@link Script} which can be executed with a
+     *      {@link JexlContext}.
+     * @throws Exception An exception can be thrown if there is a
+     *      problem parsing the script.
+     * @deprecated Create a JexlEngine and use the createScript method on that instead.
+     */
+    @Deprecated
+    public static Script createScript(String scriptText) throws Exception {
+        return (Script) EngineHolder.JEXL10.createScript(scriptText);
+    }
+
+    /**
+     * Creates a Script from a {@link File} containing valid JEXL syntax.
+     * This method parses the script and validates the syntax.
+     *
+     * @param scriptFile A {@link File} containing valid JEXL syntax.
+     *      Must not be null. Must be a readable file.
+     * @return A {@link Script} which can be executed with a
+     *      {@link JexlContext}.
+     * @throws Exception An exception can be thrown if there is a problem
+     *      parsing the script.
+     * @deprecated Create a JexlEngine and use the createScript method on that instead.
+     */
+    @Deprecated
+    public static Script createScript(File scriptFile) throws Exception {
+        return (Script) EngineHolder.JEXL10.createScript(scriptFile);
+    }
+
+    /**
+     * Creates a Script from a {@link URL} containing valid JEXL syntax.
+     * This method parses the script and validates the syntax.
+     *
+     * @param scriptUrl A {@link URL} containing valid JEXL syntax.
+     *      Must not be null. Must be a readable file.
+     * @return A {@link Script} which can be executed with a
+     *      {@link JexlContext}.
+     * @throws Exception An exception can be thrown if there is a problem
+     *      parsing the script.
+     * @deprecated Create a JexlEngine and use the createScript method on that instead.
+     */
+    @Deprecated
+    public static Script createScript(URL scriptUrl) throws Exception {
+        return (Script) EngineHolder.JEXL10.createScript(scriptUrl);
+    }
+
+    /**
+     * Creates an Expression from a String containing valid
+     * JEXL syntax.  This method parses the expression which
+     * must contain either a reference or an expression.
+     * @param expression A String containing valid JEXL syntax
+     * @return An Expression object which can be evaluated with a JexlContext
+     * @throws JexlException An exception can be thrown if there is a problem
+     *      parsing this expression, or if the expression is neither an
+     *      expression or a reference.
+     * @deprecated Create a JexlEngine and use createExpression() on that
+     */
+    @Deprecated
+    public static Expression createExpression(String expression) {
+        return (Expression) EngineHolder.JEXL10.createExpression(expression);
+    }
+}
diff --git a/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/Script.java b/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/Script.java
new file mode 100644
index 0000000..93f3089
--- /dev/null
+++ b/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/Script.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl;
+
+/**
+ * Jexl-1.x compatible script.
+ * @since 2.0
+ * @version $Id$
+ */
+public interface Script extends org.apache.commons.jexl2.Script {
+    /**
+     * Executes the script with the variables contained in the
+     * supplied {@link JexlContext}. 
+     * 
+     * @param context A JexlContext containing variables.
+     * @return The result of this script, usually the result of 
+     *      the last statement.
+     * @throws Exception on any script parse or execution error.
+     */
+    Object execute(JexlContext context) throws Exception;
+}
diff --git a/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/ScriptFactory.java b/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/ScriptFactory.java
new file mode 100644
index 0000000..6de844f
--- /dev/null
+++ b/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/ScriptFactory.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl;
+
+import org.apache.commons.jexl2.JexlEngine;
+
+/**
+ * <p>
+ * Creates {@link Script}s.  To create a JEXL Script, pass
+ * valid JEXL syntax to the static createScript() method:
+ * </p>
+ *
+ * <pre>
+ * String jexl = "y = x * 12 + 44; y = y * 4;";
+ * Script script = ScriptFactory.createScript( jexl );
+ * </pre>
+ *
+ * <p>
+ * When an {@link Script} is created, the JEXL syntax is
+ * parsed and verified.
+ * </p>
+ *
+ * <p>
+ * This is a convenience class; using an instance of a {@link JexlEngine}
+ * that serves the same purpose with more control is recommended.
+ * </p>
+ * @since 1.1
+ * @version $Id: ScriptFactory.java 884175 2009-11-25 16:23:41Z henrib $
+ * @deprecated Create a JexlEngine and use the createScript method on that instead.
+ */
+@Deprecated
+public final class ScriptFactory extends JexlOne {}
+
diff --git a/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/context/HashMapContext.java b/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/context/HashMapContext.java
new file mode 100644
index 0000000..4dd2bcf
--- /dev/null
+++ b/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/context/HashMapContext.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl.context;
+
+import org.apache.commons.jexl.JexlContext;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *  Implementation of JexlContext based on a HashMap.
+ *
+ *  @since 1.0
+ *  @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
+ *  @version $Id: HashMapContext.java 706202 2008-10-20 10:28:25Z sebb $
+ */
+public class HashMapContext extends HashMap<String,Object> implements JexlContext {
+    /** serialization version id jdk13 generated. */
+    private static final long serialVersionUID = 5715964743204418854L;
+    /**
+     * {@inheritDoc}
+     */
+    public void setVars(Map<String,Object> vars) {
+        clear();
+        putAll(vars);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Map<String,Object> getVars() {
+        return this;
+    }
+}
diff --git a/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/context/package.html b/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/context/package.html
new file mode 100644
index 0000000..70f6e48
--- /dev/null
+++ b/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/context/package.html
@@ -0,0 +1,36 @@
+<html>
+ <!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You 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.
+ -->
+ <head>
+  <title>Package Documentation for org.apache.commons.jexl.context Package</title>
+ </head>
+ <body bgcolor="white">
+  Simple JexlContext implementations.
+  <br/><br/>
+  <p>
+   <ul>
+    <li><a href="#intro">Introduction</a></li>
+   </ul>
+  </p>
+  <h2><a name="intro">Introduction</a></h2>
+  <p>
+   This package only contains one JexlContext implementation, the
+   HashMapContext.  A HashMapContext is simply an extension of
+   HashMap which implements the JexlContext interface.
+  </p>
+</body>
+</html>
diff --git a/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/package.html b/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/package.html
new file mode 100644
index 0000000..26dd052
--- /dev/null
+++ b/COMMONS_JEXL_2_0/jexl2-compat/src/main/java/org/apache/commons/jexl/package.html
@@ -0,0 +1,38 @@
+<html>
+    <!--
+       Licensed to the Apache Software Foundation (ASF) under one or more
+       contributor license agreements.  See the NOTICE file distributed with
+       this work for additional information regarding copyright ownership.
+       The ASF licenses this file to You 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.
+    -->
+    <head>
+        <title>Package Documentation for org.apache.commons.jexl Package</title>
+    </head>
+    <body bgcolor="white">
+        <h2>Jexl-1.x compatible implementation.</h2>
+        <p>
+            This package only contains classes that re-implement Jexl-1.x interfaces
+            and behaviors. This is intended to allow easier conversion to Jexl-2.0.
+        </p>
+        <p>
+            Jexl-2.0 changed a lot of APIs and behaviors, enough to warrant putting it
+            in its own org.apache.commons.jexl2 package and avoid possible "jar-hell" cases.
+            Those could have occured if someone was trying to use both Jexl-2.0 and Jexl-1.x.
+        </p>
+        <p>
+            This package contains the original Jexl-1.x main API namely ScriptFactory, ExpressionFactory,
+            Script, Expression, JexlContext and JexlHelper. It is not 100% compatible with
+            the original Jexl-1.x codeline but should be close enough for "casual" usage.
+        </p>
+    </body>
+</html>
diff --git a/COMMONS_JEXL_2_0/jexl2-compat/src/test/java/org/apache/commons/jexl/ScriptTest.java b/COMMONS_JEXL_2_0/jexl2-compat/src/test/java/org/apache/commons/jexl/ScriptTest.java
new file mode 100644
index 0000000..5765942
--- /dev/null
+++ b/COMMONS_JEXL_2_0/jexl2-compat/src/test/java/org/apache/commons/jexl/ScriptTest.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl;
+import junit.framework.TestCase;
+import java.io.File;
+import java.net.URL;
+
+/**
+ * Tests for Script
+ * @since 1.1
+ */
+public class ScriptTest extends TestCase {
+    static final String TEST1 =  "../src/test/scripts/test1.jexl";
+
+    // test class for testScriptUpdatesContext
+    // making this class private static will cause the test to fail.
+    // this is due to unusual code in ClassMap.getAccessibleMethods(Class)
+    // that treats non-public classes in a specific way. Why getAccessibleMethods
+    // does this is not known yet.
+    public static class Tester {
+        private String code;
+        public String getCode () { 
+            return code; 
+        }
+        public void setCode(String c) {
+            code = c;
+        }
+    }
+    /**
+     * Create a new test case.
+     * @param name case name
+     */
+    public ScriptTest(String name) {
+        super(name);
+    }
+
+    /**
+     * Test creating a script from a string.
+     */
+    public void testSimpleScript() throws Exception {
+        String code = "while (x < 10) x = x + 1;";
+        Script s = ScriptFactory.createScript(code);
+        JexlContext jc = JexlHelper.createContext();
+        jc.getVars().put("x", new Integer(1));
+    
+        Object o = s.execute(jc);
+        assertEquals("Result is wrong", new Integer(10), o);
+        assertEquals("getText is wrong", code, s.getText());
+    }
+
+    public void testScriptFromFile() throws Exception {
+        File testScript = new File(TEST1);
+        Script s = ScriptFactory.createScript(testScript);
+        JexlContext jc = JexlHelper.createContext();
+        jc.getVars().put("out", System.out);
+        Object result = s.execute(jc);
+        assertNotNull("No result", result);
+        assertEquals("Wrong result", new Integer(7), result);
+    }
+
+    public void testScriptFromURL() throws Exception {
+        URL testUrl = new File(TEST1).toURI().toURL();
+        Script s = ScriptFactory.createScript(testUrl);
+        JexlContext jc = JexlHelper.createContext();
+        jc.getVars().put("out", System.out);
+        Object result = s.execute(jc);
+        assertNotNull("No result", result);
+        assertEquals("Wrong result", new Integer(7), result);
+    }
+
+    public void testScriptUpdatesContext() throws Exception {
+        String jexlCode = "resultat.setCode('OK')";
+        Expression e = ExpressionFactory.createExpression(jexlCode);
+        Script s = ScriptFactory.createScript(jexlCode);
+
+        Tester resultatJexl = new Tester();
+        JexlContext jc = JexlHelper.createContext();
+        jc.getVars().put("resultat", resultatJexl);
+
+        resultatJexl.setCode("");
+        e.evaluate(jc);
+        assertEquals("OK", resultatJexl.getCode());
+        resultatJexl.setCode("");
+        s.execute(jc);
+        assertEquals("OK", resultatJexl.getCode());
+    }
+
+}
diff --git a/COMMONS_JEXL_2_0/pom.xml b/COMMONS_JEXL_2_0/pom.xml
new file mode 100644
index 0000000..15f44e1
--- /dev/null
+++ b/COMMONS_JEXL_2_0/pom.xml
@@ -0,0 +1,330 @@
+<?xml version="1.0"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You 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.
+-->
+<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">
+    <parent>
+        <groupId>org.apache.commons</groupId>
+        <artifactId>commons-parent</artifactId>
+        <version>12</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.apache.commons</groupId>
+    <artifactId>commons-jexl</artifactId>
+    <version>2.0</version>
+    <name>Commons JEXL</name>
+    <inceptionYear>2001</inceptionYear>
+    <description>Jexl is an implementation of the JSTL Expression Language with extensions.</description>
+    <url>http://commons.apache.org/jexl/</url>
+
+    <issueManagement>
+        <system>jira</system>
+        <url>http://issues.apache.org/jira/browse/JEXL</url>
+    </issueManagement>
+
+    <scm>
+        <connection>scm:svn:http://svn.apache.org/repos/asf/commons/proper/jexl/tags/COMMONS_JEXL_2_0-RC6</connection>
+        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/commons/proper/jexl/tags/COMMONS_JEXL_2_0-RC6</developerConnection>
+        <url>http://svn.apache.org/viewvc/commons/proper/jexl/tags/COMMONS_JEXL_2_0-RC6</url>
+    </scm>
+
+    <developers>
+        <developer>
+            <name>dIon Gillard</name>
+            <id>dion</id>
+            <email>dion AT apache DOT org</email>
+            <organization>Apache Software Foundation</organization>
+        </developer>
+        <developer>
+            <name>Geir Magnusson Jr.</name>
+            <id>geirm</id>
+            <email>geirm AT apache DOT org</email>
+            <organization>independent</organization>
+        </developer>
+        <developer>
+            <name>Tim O'Brien</name>
+            <id>tobrien</id>
+            <email>tobrien AT apache DOT org</email>
+            <organization>independent</organization>
+        </developer>
+        <developer>
+            <name>Peter Royal</name>
+            <id>proyal</id>
+            <email>proyal AT apache DOT org</email>
+            <organization>Apache Software Foundation</organization>
+        </developer>
+        <developer>
+            <name>James Strachan</name>
+            <id>jstrachan</id>
+            <email>jstrachan AT apache DOT org</email>
+            <organization>SpiritSoft, Inc.</organization>
+        </developer>
+        <developer>
+            <name>Rahul Akolkar</name>
+            <id>rahul</id>
+            <email>rahul AT apache DOT org</email>
+            <organization>Apache Software Foundation</organization>
+        </developer>
+        <developer>
+            <name>Sebastian Bazley</name>
+            <id>sebb</id>
+            <email>sebb AT apache DOT org</email>
+        </developer>
+        <developer>
+            <name>Henri Biestro</name>
+            <id>henrib</id>
+            <email>henrib AT apache DOT org</email>
+        </developer>
+    </developers>
+
+    <dependencies>
+        <dependency>
+            <groupId>commons-logging</groupId>
+            <artifactId>commons-logging</artifactId>
+            <version>1.1.1</version>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>3.8.2</version>
+            <scope>test</scope>
+        </dependency>
+        <!-- For JSR-223 API -->
+        <dependency>
+            <groupId>org.apache.bsf</groupId>
+            <artifactId>bsf-api</artifactId>
+            <version>[3.0-beta3,)</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <properties>
+        <maven.compile.source>1.5</maven.compile.source>
+        <maven.compile.target>1.5</maven.compile.target>
+        <commons.componentid>jexl</commons.componentid>
+        <commons.release.version>2.0</commons.release.version>
+        <!-- The RC version used in the staging repository URL. -->
+        <commons.rc.version>RC6</commons.rc.version>
+        <commons.binary.suffix />
+        <commons.jira.id>JEXL</commons.jira.id>
+        <commons.jira.pid>12310479</commons.jira.pid>
+        <!-- Temp fix until parent POM is updated -->
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+    </properties>
+
+    <build>
+        <!-- temporarily override the parent POM (v 11) until that is updated -->
+        <resources>
+            <resource>
+                <directory>${basedir}</directory>
+                <targetPath>META-INF</targetPath>
+                <includes>
+                    <include>NOTICE.txt</include>
+                    <include>LICENSE.txt</include>
+                </includes>
+            </resource>
+            <!-- This is the default, but is currently missing from the parent POM (v11) -->
+            <resource>
+                <directory>src/main/resources</directory>
+            </resource>
+        </resources>
+        <plugins>
+            <!-- workaround MRELEASE-424 when publishing -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-release-plugin</artifactId>
+                <configuration>
+                    <mavenExecutorId>forked-path</mavenExecutorId>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <includes>
+                        <include>**/*Test.java</include>
+                    </includes>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <configuration>
+                    <descriptors>
+                        <descriptor>src/main/assembly/bin.xml</descriptor>
+                        <descriptor>src/main/assembly/src.xml</descriptor>
+                    </descriptors>
+                    <tarLongFileMode>gnu</tarLongFileMode>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>javacc-maven-plugin</artifactId>
+                <version>2.6</version>
+                <executions>
+                    <execution>
+                        <id>jexl-jjtree</id>
+                        <configuration>
+                            <sourceDirectory>${basedir}/src/main/java/org/apache/commons/jexl2/parser</sourceDirectory>
+                            <outputDirectory>${project.build.directory}/generated-sources/java</outputDirectory>
+                            <timestampDirectory>${project.build.directory}/generated-sources/javacc-timestamp</timestampDirectory>
+                            <packageName>org.apache.commons.jexl2.parser</packageName>
+                        </configuration>
+                        <goals>
+                            <goal>jjtree-javacc</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <!--
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>clirr-maven-plugin</artifactId>
+                <version>2.2.2</version>
+                <configuration>
+                    <comparisonArtifacts>
+                        <comparisonArtifact>
+                            <groupId>commons-jexl</groupId>
+                            <artifactId>commons-jexl</artifactId>
+                            <version>1.1</version>
+                        </comparisonArtifact>
+                    </comparisonArtifacts>
+                </configuration>
+            </plugin>
+            -->
+        </plugins>
+    </build>
+
+    <reporting>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-changes-plugin</artifactId>
+                <version>2.1</version>
+                <configuration>
+                    <xmlPath>${basedir}/xdocs/changes.xml</xmlPath>
+                    <issueLinkTemplate>%URL%/%ISSUE%</issueLinkTemplate>
+                </configuration>
+                <reportSets>
+                    <reportSet>
+                        <reports>
+                            <report>changes-report</report>
+                        </reports>
+                    </reportSet>
+                </reportSets>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-checkstyle-plugin</artifactId>
+                <version>2.3</version>
+                <configuration>
+                    <configLocation>${basedir}/src/main/config/checkstyle.xml</configLocation>
+                    <excludes>org/apache/commons/jexl2/parser/*.java</excludes>
+                    <headerFile>${basedir}/src/main/config/header.txt</headerFile>
+                    <enableRulesSummary>false</enableRulesSummary>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>cobertura-maven-plugin</artifactId>
+                <version>2.3</version>
+                <configuration>
+                    <instrumentation>
+                        <excludes>
+                            <exclude>org/apache/commons/jexl2/parser/*.class</exclude>
+                            <exclude>org/apache/commons/jexl2/**/*Test.class</exclude>
+                        </excludes>
+                    </instrumentation>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>findbugs-maven-plugin</artifactId>
+                <version>2.1</version>
+                <configuration>
+                        <excludes>
+                            <exclude>org/apache/commons/jexl2/parser/*.class</exclude>
+                            <exclude>org/apache/commons/jexl2/**/*Test.class</exclude>
+                        </excludes>
+                    <xmlOutput>true</xmlOutput>
+                    <!-- Optional directory to put findbugs xdoc xml report -->
+                    <xmlOutputDirectory>target/site</xmlOutputDirectory>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-pmd-plugin</artifactId>
+                <version>2.4</version>
+                <configuration>
+                    <targetJdk>1.5</targetJdk>
+                    <excludes>
+                        <exclude>org/apache/commons/jexl2/parser/*.class</exclude>
+                        <exclude>org/apache/commons/jexl2/**/*Test.class</exclude>
+                    </excludes>
+                    <rulesets>
+                        <ruleset>/rulesets/braces.xml</ruleset>
+                        <ruleset>/rulesets/unusedcode.xml</ruleset>
+                        <ruleset>/rulesets/imports.xml</ruleset>
+                        <ruleset>/rulesets/codesize.xml</ruleset>
+                        <ruleset>/rulesets/coupling.xml</ruleset>
+                        <ruleset>/rulesets/design.xml</ruleset>
+                        <ruleset>/rulesets/strings.xml</ruleset>
+                    </rulesets>
+                </configuration>
+            </plugin>
+            <!--
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>clirr-maven-plugin</artifactId>
+                <version>2.2.2</version>
+                <configuration>
+                    <comparisonArtifacts>
+                        <comparisonArtifact>
+                            <groupId>commons-jexl</groupId>
+                            <artifactId>commons-jexl</artifactId>
+                            <version>1.1</version>
+                        </comparisonArtifact>
+                    </comparisonArtifacts>
+                </configuration>
+            </plugin>
+            -->
+        </plugins>
+    </reporting>
+
+
+    <distributionManagement>
+        <site>
+            <id>website</id>
+            <name>Apache Website</name>
+            <url>${commons.deployment.protocol}://people.apache.org/www/commons.apache.org/jexl/</url>
+        </site>
+    </distributionManagement>
+
+    <profiles>
+        <profile>
+            <id>rc</id>
+            <distributionManagement>
+                <!-- Cannot define in parent ATM, see COMMONSSITE-26 -->
+                <site>
+                    <id>apache.website</id>
+                    <name>Apache Commons Release Candidate Staging Site</name>
+                    <url>${commons.deployment.protocol}://people.apache.org/www/people.apache.org/builds/commons/${commons.componentid}/${commons.release.version}/${commons.rc.version}/site</url>
+                </site>
+            </distributionManagement>
+        </profile>
+    </profiles>
+
+</project>
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/main/assembly/bin.xml b/COMMONS_JEXL_2_0/src/main/assembly/bin.xml
new file mode 100644
index 0000000..5762291
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/assembly/bin.xml
@@ -0,0 +1,44 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+-->
+<assembly>
+    <id>bin</id>
+    <formats>
+        <format>tar.gz</format>
+        <format>zip</format>
+    </formats>
+    <includeSiteDirectory>false</includeSiteDirectory>
+    <fileSets>
+        <fileSet>
+            <includes>
+                <include>LICENSE.txt</include>
+                <include>NOTICE.txt</include>
+                <include>RELEASE-NOTES.txt</include>
+            </includes>
+        </fileSet>
+        <fileSet>
+            <directory>target</directory>
+            <outputDirectory></outputDirectory>
+            <includes>
+                <include>*.jar</include>
+            </includes>
+        </fileSet>
+        <fileSet>
+            <directory>target/site/apidocs</directory>
+            <outputDirectory>apidocs</outputDirectory>
+        </fileSet>
+    </fileSets>
+</assembly>
diff --git a/COMMONS_JEXL_2_0/src/main/assembly/src.xml b/COMMONS_JEXL_2_0/src/main/assembly/src.xml
new file mode 100644
index 0000000..f2b63ea
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/assembly/src.xml
@@ -0,0 +1,40 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+-->
+<assembly>
+    <id>src</id>
+    <formats>
+        <format>tar.gz</format>
+        <format>zip</format>
+    </formats>
+    <baseDirectory>${artifactId}-${commons.release.version}-src</baseDirectory>
+    <fileSets>
+        <fileSet>
+            <includes>
+                <include>LICENSE.txt</include>
+                <include>NOTICE.txt</include>
+                <include>pom.xml</include>
+                <include>RELEASE-NOTES.txt</include>
+            </includes>
+        </fileSet>
+        <fileSet>
+            <directory>src</directory>
+        </fileSet>
+        <fileSet>
+            <directory>xdocs</directory>
+        </fileSet>
+    </fileSets>
+</assembly>
diff --git a/COMMONS_JEXL_2_0/src/main/config/checkstyle.xml b/COMMONS_JEXL_2_0/src/main/config/checkstyle.xml
new file mode 100644
index 0000000..159e0a9
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/config/checkstyle.xml
@@ -0,0 +1,220 @@
+<?xml version="1.0"?>
+<!-- 
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+ -->
+
+<!DOCTYPE module PUBLIC
+    "-//Puppy Crawl//DTD Check Configuration 1.1//EN"
+    "http://www.puppycrawl.com/dtds/configuration_1_1.dtd">
+
+<!--
+
+  Checkstyle configuration that checks the sun coding conventions from:
+
+    - the Java Language Specification at
+      http://java.sun.com/docs/books/jls/second_edition/html/index.html
+
+    - the Sun Code Conventions at http://java.sun.com/docs/codeconv/
+
+    - the Javadoc guidelines at
+      http://java.sun.com/j2se/javadoc/writingdoccomments/index.html
+
+    - the JDK Api documentation http://java.sun.com/j2se/docs/api/index.html
+
+    - some best practices
+
+  Checkstyle is very configurable. Be sure to read the documentation at
+  http://checkstyle.sf.net (or in your downloaded distribution).
+
+  Most Checks are configurable, be sure to consult the documentation.
+
+  To completely disable a check, just comment it out or delete it from the file.
+
+  Finally, it is worth reading the documentation.
+
+-->
+
+<module name="Checker">
+
+    <!-- Checks that a package.html file exists for each package.     -->
+    <!-- See http://checkstyle.sf.net/config_javadoc.html#PackageHtml -->
+    <module name="PackageHtml"/>
+
+    <!-- Checks whether files end with a new line.                        -->
+    <!-- See http://checkstyle.sf.net/config_misc.html#NewlineAtEndOfFile -->
+    <!-- JEXL-20: tried every combination on a Mac to no avail...
+    <module name="NewlineAtEndOfFile">
+        <property name="lineSeparator" value="lf"/>
+    </module>
+    -->
+
+    <!-- Checks that property files contain the same keys.         -->
+    <!-- See http://checkstyle.sf.net/config_misc.html#Translation -->
+    <module name="Translation"/>
+
+
+    <module name="TreeWalker">
+
+        <property name="cacheFile" value="${checkstyle.cache.file}"/>
+
+        <!-- Checks for Javadoc comments.                     -->
+        <!-- See http://checkstyle.sf.net/config_javadoc.html -->
+        <!-- JEXL-20: many of those by API choice -->
+        <module name="JavadocMethod">
+           <property name="allowUndeclaredRTE" value="true"/>
+        </module>
+        <module name="JavadocType"/>
+        <module name="JavadocVariable"/>
+        <module name="JavadocStyle"/>
+
+
+        <!-- Checks for Naming Conventions.                  -->
+        <!-- See http://checkstyle.sf.net/config_naming.html -->
+        <module name="ConstantName"/>
+        <module name="LocalFinalVariableName"/>
+        <module name="LocalVariableName"/>
+        <module name="MemberName"/>
+        <module name="MethodName"/>
+        <module name="PackageName"/>
+        <module name="ParameterName"/>
+        <module name="StaticVariableName"/>
+        <module name="TypeName"/>
+
+
+        <!-- Checks for Headers                              -->
+        <!-- See http://checkstyle.sf.net/config_header.html -->
+        <module name="Header">
+            <!-- The follow property value demonstrates the ability     -->
+            <!-- to have access to ANT properties. In this case it uses -->
+            <!-- the ${basedir} property to allow Checkstyle to be run  -->
+            <!-- from any directory within a project. See property      -->
+            <!-- expansion,                                             -->
+            <!-- http://checkstyle.sf.net/config.html#properties        -->
+            <!-- <property                                              -->
+            <!--     name="headerFile"                                  -->
+            <!--     value="${basedir}/java.header"/>                   -->
+            <property name="headerFile" value="${checkstyle.header.file}"/>
+            <property name="ignoreLines" value="2"/>
+        </module>
+
+        <!-- Following interprets the header file as regular expressions. -->
+        <!-- <module name="RegexpHeader"/>                                -->
+
+
+        <!-- Checks for imports                              -->
+        <!-- See http://checkstyle.sf.net/config_import.html -->
+        <module name="AvoidStarImport"/>
+        <module name="IllegalImport"/> <!-- defaults to sun.* packages -->
+        <module name="RedundantImport"/>
+        <module name="UnusedImports"/>
+
+
+        <!-- Checks for Size Violations.                    -->
+        <!-- See http://checkstyle.sf.net/config_sizes.html -->
+        <module name="FileLength"/>
+        <module name="LineLength">
+            <!--  JEXL: added max, ignore @version and @see -->
+            <property name="max" value="120"/>
+            <property name="ignorePattern" value="@version"/>
+            <property name="ignorePattern" value="@see"/>
+        </module>
+        <module name="MethodLength"/>
+        <module name="ParameterNumber"/>
+
+
+        <!-- Checks for whitespace                               -->
+        <!-- See http://checkstyle.sf.net/config_whitespace.html -->
+        <module name="EmptyForIteratorPad"/>
+        <!-- <module name="GenericWhitespace"/> -->
+        <!-- JEXL-20: because the above is not yet supported through Maven plugin, disable others-->
+        <!-- <module name="NoWhitespaceAfter"/> -->
+        <!-- <module name="NoWhitespaceBefore"/> -->
+        <!-- <module name="WhitespaceAfter"/> -->
+        <!-- <module name="WhitespaceAround"/> -->
+        <module name="OperatorWrap"/>
+        <module name="ParenPad"/>
+        <module name="TypecastParenPad"/>
+        <module name="TabCharacter"/>
+
+
+        <!-- Modifier Checks                                    -->
+        <!-- See http://checkstyle.sf.net/config_modifiers.html -->
+        <module name="ModifierOrder"/>
+        <module name="RedundantModifier"/>
+
+
+        <!-- Checks for blocks. You know, those {}'s         -->
+        <!-- See http://checkstyle.sf.net/config_blocks.html -->
+        <module name="AvoidNestedBlocks"/>
+        <module name="EmptyBlock"/>
+        <module name="LeftCurly"/>
+        <module name="NeedBraces"/>
+        <module name="RightCurly"/>
+
+
+        <!-- Checks for common coding problems               -->
+        <!-- See http://checkstyle.sf.net/config_coding.html -->
+        <!-- JEXL: module name="AvoidInlineConditionals"/-->
+        <module name="DoubleCheckedLocking"/>
+        <module name="EmptyStatement"/>
+        <module name="EqualsHashCode"/>
+        <module name="HiddenField"/>
+        <module name="IllegalInstantiation"/>
+        <module name="InnerAssignment"/>
+        <module name="MagicNumber">
+            <property name="ignoreNumbers" value="-1, 0, 1, 2, 3"/>
+        </module>
+        <module name="MissingSwitchDefault"/>
+        <!-- JEXL-20: used in introspection -->
+        <!--<module name="RedundantThrows"/>-->
+        <module name="SimplifyBooleanExpression"/>
+        <module name="SimplifyBooleanReturn"/>
+
+        <!-- Checks for class design                         -->
+        <!-- See http://checkstyle.sf.net/config_design.html -->
+        <!-- JEXL: module name="DesignForExtension"/-->
+        <!-- JEXL: module name="FinalClass"/-->
+        <!-- JEXL: module name="HideUtilityClassConstructor"/-->
+        <module name="InterfaceIsType"/>
+        <module name="VisibilityModifier">
+            <property name="protectedAllowed" value="true"/>
+        </module>
+
+
+        <!-- Miscellaneous other checks.                   -->
+        <!-- See http://checkstyle.sf.net/config_misc.html -->
+        <module name="ArrayTypeStyle"/>
+        <!-- JEXL: module name="FinalParameters"/-->
+        <!-- JEXL: module name="GenericIllegalRegexp">
+            <property name="format" value="\s+$"/>
+            <property name="message" value="Line has trailing spaces."/>
+        </module-->
+        <!--module name="TodoComment"/-->
+        <module name="UpperEll"/>
+        
+        <!-- used for suppression comment filter -->
+        <module name="FileContentsHolder"/>
+    </module>
+
+    <module name="SuppressionCommentFilter">
+        <property name="offCommentFormat" value="CSOFF\: ([\w\|]+)"/>
+        <property name="onCommentFormat" value="CSON\: ([\w\|]+)"/>
+        <property name="checkFormat" value="$1"/>
+    </module>
+
+</module>
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/main/config/header.txt b/COMMONS_JEXL_2_0/src/main/config/header.txt
new file mode 100644
index 0000000..f974c9a
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/config/header.txt
@@ -0,0 +1,16 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/DebugInfo.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/DebugInfo.java
new file mode 100644
index 0000000..4306d0a
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/DebugInfo.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+
+/**
+ * Little class to carry in info such as a url/file name, line and column for
+ * information error reporting from the uberspector implementations.
+ * @version $Id$
+ */
+public class DebugInfo implements JexlInfo {
+    /** line number. */
+    private final int line;
+    /** column number. */
+    private final int column;
+    /** name. */
+    private final String name;
+    /** 
+     * Create info.
+     * @param tn template name
+     * @param l line number
+     * @param c column
+     */
+    public DebugInfo(String tn, int l, int c) {
+        name = tn;
+        line = l;
+        column = c;
+    }
+
+    /**
+     * Formats this info in the form 'name&#064;line:column'.
+     * @return the formatted info
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder(name != null? name : "");
+        if (line > 0) {
+            sb.append("@");
+            sb.append(line);
+            if (column > 0) {
+                sb.append(":");
+                sb.append(column);
+            }
+        }
+        return sb.toString();
+    }
+    
+    /** {@inheritDoc} */
+    public String debugString() {
+        return toString();
+    }
+
+    /**
+     * Gets the file/script/url name.
+     * @return template name
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Gets the line number.
+     * @return line number.
+     */
+    public int getLine() {
+        return line;
+    }
+
+    /**
+     * Gets the column number.
+     * @return the column.
+     */
+    public int getColumn() {
+        return column;
+    }
+}
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/Debugger.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/Debugger.java
new file mode 100644
index 0000000..4048e4b
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/Debugger.java
@@ -0,0 +1,641 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+import org.apache.commons.jexl2.parser.ASTAdditiveNode;
+import org.apache.commons.jexl2.parser.ASTAdditiveOperator;
+import org.apache.commons.jexl2.parser.ASTAndNode;
+import org.apache.commons.jexl2.parser.ASTAmbiguous;
+import org.apache.commons.jexl2.parser.ASTArrayAccess;
+import org.apache.commons.jexl2.parser.ASTArrayLiteral;
+import org.apache.commons.jexl2.parser.ASTAssignment;
+import org.apache.commons.jexl2.parser.ASTBitwiseAndNode;
+import org.apache.commons.jexl2.parser.ASTBitwiseComplNode;
+import org.apache.commons.jexl2.parser.ASTBitwiseOrNode;
+import org.apache.commons.jexl2.parser.ASTBitwiseXorNode;
+import org.apache.commons.jexl2.parser.ASTBlock;
+import org.apache.commons.jexl2.parser.ASTConstructorNode;
+import org.apache.commons.jexl2.parser.ASTDivNode;
+import org.apache.commons.jexl2.parser.ASTEQNode;
+import org.apache.commons.jexl2.parser.ASTERNode;
+import org.apache.commons.jexl2.parser.ASTEmptyFunction;
+import org.apache.commons.jexl2.parser.ASTFalseNode;
+import org.apache.commons.jexl2.parser.ASTFloatLiteral;
+import org.apache.commons.jexl2.parser.ASTForeachStatement;
+import org.apache.commons.jexl2.parser.ASTFunctionNode;
+import org.apache.commons.jexl2.parser.ASTGENode;
+import org.apache.commons.jexl2.parser.ASTGTNode;
+import org.apache.commons.jexl2.parser.ASTIdentifier;
+import org.apache.commons.jexl2.parser.ASTIfStatement;
+import org.apache.commons.jexl2.parser.ASTIntegerLiteral;
+import org.apache.commons.jexl2.parser.ASTJexlScript;
+import org.apache.commons.jexl2.parser.ASTLENode;
+import org.apache.commons.jexl2.parser.ASTLTNode;
+import org.apache.commons.jexl2.parser.ASTMapEntry;
+import org.apache.commons.jexl2.parser.ASTMapLiteral;
+import org.apache.commons.jexl2.parser.ASTMethodNode;
+import org.apache.commons.jexl2.parser.ASTModNode;
+import org.apache.commons.jexl2.parser.ASTMulNode;
+import org.apache.commons.jexl2.parser.ASTNENode;
+import org.apache.commons.jexl2.parser.ASTNRNode;
+import org.apache.commons.jexl2.parser.ASTNotNode;
+import org.apache.commons.jexl2.parser.ASTNullLiteral;
+import org.apache.commons.jexl2.parser.ASTOrNode;
+import org.apache.commons.jexl2.parser.ASTReference;
+import org.apache.commons.jexl2.parser.ASTSizeFunction;
+import org.apache.commons.jexl2.parser.ASTSizeMethod;
+import org.apache.commons.jexl2.parser.ASTStringLiteral;
+import org.apache.commons.jexl2.parser.ASTTernaryNode;
+import org.apache.commons.jexl2.parser.ASTTrueNode;
+import org.apache.commons.jexl2.parser.ASTUnaryMinusNode;
+import org.apache.commons.jexl2.parser.ASTWhileStatement;
+import org.apache.commons.jexl2.parser.JexlNode;
+import org.apache.commons.jexl2.parser.SimpleNode;
+
+import org.apache.commons.jexl2.parser.ParserVisitor;
+
+/**
+ * Helps pinpoint the cause of problems in expressions that fail during evaluation.
+ * <p>
+ * It rebuilds an expression string from the tree and the start/end offsets of the cause
+ * in that string.
+ * </p>
+ * This implies that exceptions during evaluation do allways carry the node that's causing
+ * the error.
+ * @since 2.0
+ */
+final class Debugger implements ParserVisitor {
+    /** The builder to compose messages. */
+    private final StringBuilder builder;
+    /** The cause of the issue to debug. */
+    private JexlNode cause;
+    /** The starting character location offset of the cause in the builder. */
+    private int start;
+    /** The ending character location offset of the cause in the builder. */
+    private int end;
+
+    /**
+     * Creates a Debugger.
+     */
+    Debugger() {
+        builder = new StringBuilder();
+        cause = null;
+        start = 0;
+        end = 0;
+    }
+
+    /**
+     * Seeks the location of an error cause (a node) in an expression.
+     * @param node the node to debug
+     * @return true if the cause was located, false otherwise
+     */
+    public boolean debug(JexlNode node) {
+        start = 0;
+        end = 0;
+        if (node != null) {
+            builder.setLength(0);
+            this.cause = node;
+            // make arg cause become the root cause
+            JexlNode root = node;
+            while (root.jjtGetParent() != null) {
+                root = root.jjtGetParent();
+            }
+            root.jjtAccept(this, null);
+        }
+        return end > 0;
+    }
+
+    /**
+     * @return The rebuilt expression
+     */
+    public String data() {
+        return builder.toString();
+    }
+
+    /**
+     * @return The starting offset location of the cause in the expression
+     */
+    public int start() {
+        return start;
+    }
+
+    /**
+     * @return The end offset location of the cause in the expression
+     */
+    public int end() {
+        return end;
+    }
+
+    /**
+     * Checks if a child node is the cause to debug &amp; adds its representation
+     * to the rebuilt expression.
+     * @param node the child node
+     * @param data visitor pattern argument
+     * @return visitor pattern value
+     */
+    private Object accept(JexlNode node, Object data) {
+        if (node == cause) {
+            start = builder.length();
+        }
+        Object value = node.jjtAccept(this, data);
+        if (node == cause) {
+            end = builder.length();
+        }
+        return value;
+    }
+
+    /**
+     * Adds a statement node to the rebuilt expression.
+     * @param child the child node
+     * @param data visitor pattern argument
+     * @return visitor pattern value
+     */
+    private Object acceptStatement(JexlNode child, Object data) {
+        Object value = accept(child, data);
+        // blocks, if, for & while dont need a ';' at end
+        if (child instanceof ASTBlock
+            || child instanceof ASTIfStatement
+            || child instanceof ASTForeachStatement
+            || child instanceof ASTWhileStatement) {
+            return value;
+        }
+        builder.append(";");
+        return value;
+    }
+
+    /**
+     * Checks if a terminal node is the the cause to debug &amp; adds its
+     * representation to the rebuilt expression.
+     * @param node the child node
+     * @param image the child node token image (may be null)
+     * @param data visitor pattern argument
+     * @return visitor pattern value
+     */
+    private Object check(JexlNode node, String image, Object data) {
+        if (node == cause) {
+            start = builder.length();
+        }
+        if (image != null) {
+            builder.append(image);
+        } else {
+            builder.append(node.toString());
+        }
+        if (node == cause) {
+            end = builder.length();
+        }
+        return data;
+    }
+
+    /**
+     * Checks if the children of a node using infix notation is the cause to debug,
+     * adds their representation to the rebuilt expression.
+     * @param node the child node
+     * @param infix the child node token
+     * @param paren whether the child should be parenthesized
+     * @param data visitor pattern argument
+     * @return visitor pattern value
+     */
+    private Object infixChildren(JexlNode node, String infix, boolean paren, Object data) {
+        int num = node.jjtGetNumChildren(); //child.jjtGetNumChildren() > 1;
+        if (paren) {
+            builder.append("(");
+        }
+        for (int i = 0; i < num; ++i) {
+            if (i > 0) {
+                builder.append(infix);
+            }
+            accept(node.jjtGetChild(i), data);
+        }
+        if (paren) {
+            builder.append(")");
+        }
+        return data;
+    }
+
+    /**
+     * Checks if the child of a node using prefix notation is the cause to debug,
+     * adds their representation to the rebuilt expression.
+     * @param node the node
+     * @param prefix the node token
+     * @param data visitor pattern argument
+     * @return visitor pattern value
+     */
+    private Object prefixChild(JexlNode node, String prefix, Object data) {
+        boolean paren = node.jjtGetChild(0).jjtGetNumChildren() > 1;
+        builder.append(prefix);
+        if (paren) {
+            builder.append("(");
+        }
+        accept(node.jjtGetChild(0), data);
+        if (paren) {
+            builder.append(")");
+        }
+        return data;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTAdditiveNode node, Object data) {
+        // need parenthesis if not in operator precedence order
+        boolean paren = node.jjtGetParent() instanceof ASTMulNode
+                        || node.jjtGetParent() instanceof ASTDivNode
+                        || node.jjtGetParent() instanceof ASTModNode;
+        int num = node.jjtGetNumChildren(); //child.jjtGetNumChildren() > 1;
+        if (paren) {
+            builder.append("(");
+        }
+        accept(node.jjtGetChild(0), data);
+        for (int i = 1; i < num; ++i) {
+            accept(node.jjtGetChild(i), data);
+        }
+        if (paren) {
+            builder.append(")");
+        }
+        return data;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTAdditiveOperator node, Object data) {
+        builder.append(' ');
+        builder.append(node.image);
+        builder.append(' ');
+        return data;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTAndNode node, Object data) {
+        return infixChildren(node, " && ", false, data);
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTArrayAccess node, Object data) {
+        accept(node.jjtGetChild(0), data);
+        int num = node.jjtGetNumChildren();
+        for (int i = 1; i < num; ++i) {
+            builder.append("[");
+            accept(node.jjtGetChild(i), data);
+            builder.append("]");
+        }
+        return data;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTArrayLiteral node, Object data) {
+        int num = node.jjtGetNumChildren();
+        builder.append("[ ");
+        accept(node.jjtGetChild(0), data);
+        for (int i = 1; i < num; ++i) {
+            builder.append(", ");
+            accept(node.jjtGetChild(i), data);
+        }
+        builder.append(" ]");
+        return data;
+    }
+    
+    /** {@inheritDoc} */
+    public Object visit(ASTAssignment node, Object data) {
+        return infixChildren(node, " = ", false, data);
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTBitwiseAndNode node, Object data) {
+        return infixChildren(node, " & ", false, data);
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTBitwiseComplNode node, Object data) {
+        return prefixChild(node, "~", data);
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTBitwiseOrNode node, Object data) {
+        boolean paren = node.jjtGetParent() instanceof ASTBitwiseAndNode;
+        return infixChildren(node, " | ", paren, data);
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTBitwiseXorNode node, Object data) {
+        boolean paren = node.jjtGetParent() instanceof ASTBitwiseAndNode;
+        return infixChildren(node, " ^ ", paren, data);
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTBlock node, Object data) {
+        builder.append("{ ");
+        int num = node.jjtGetNumChildren();
+        for (int i = 0; i < num; ++i) {
+            JexlNode child = node.jjtGetChild(i);
+            acceptStatement(child, data);
+        }
+        builder.append(" }");
+        return data;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTDivNode node, Object data) {
+        return infixChildren(node, " / ", false, data);
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTEmptyFunction node, Object data) {
+        builder.append("empty(");
+        accept(node.jjtGetChild(0), data);
+        builder.append(")");
+        return data;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTEQNode node, Object data) {
+        return infixChildren(node, " == ", false, data);
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTERNode node, Object data) {
+        return infixChildren(node, " =~ ", false, data);
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTFalseNode node, Object data) {
+        return check(node, "false", data);
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTFloatLiteral node, Object data) {
+        return check(node, node.image, data);
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTForeachStatement node, Object data) {
+        builder.append("for(");
+        accept(node.jjtGetChild(0), data);
+        builder.append(" : ");
+        accept(node.jjtGetChild(1), data);
+        builder.append(") ");
+        if (node.jjtGetNumChildren() > 2) {
+            acceptStatement(node.jjtGetChild(2), data);
+        } else {
+            builder.append(';');
+        }
+        return data;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTGENode node, Object data) {
+        return infixChildren(node, " >= ", false, data);
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTGTNode node, Object data) {
+        return infixChildren(node, " > ", false, data);
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTIdentifier node, Object data) {
+        return check(node, node.image, data);
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTIfStatement node, Object data) {
+        builder.append("if (");
+        accept(node.jjtGetChild(0), data);
+        builder.append(") ");
+        if (node.jjtGetNumChildren() > 1) {
+            acceptStatement(node.jjtGetChild(1), data);
+            if (node.jjtGetNumChildren() > 2) {
+                builder.append(" else ");
+                acceptStatement(node.jjtGetChild(2), data);
+            } else {
+                builder.append(';');
+            }
+        } else {
+            builder.append(';');
+        }
+        return data;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTIntegerLiteral node, Object data) {
+        return check(node, node.image, data);
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTJexlScript node, Object data) {
+        int num = node.jjtGetNumChildren();
+        for (int i = 0; i < num; ++i) {
+            JexlNode child = node.jjtGetChild(i);
+            acceptStatement(child, data);
+        }
+        return data;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTLENode node, Object data) {
+        return infixChildren(node, " <= ", false, data);
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTLTNode node, Object data) {
+        return infixChildren(node, " < ", false, data);
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTMapEntry node, Object data) {
+        accept(node.jjtGetChild(0), data);
+        builder.append(" : ");
+        accept(node.jjtGetChild(1), data);
+        return data;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTMapLiteral node, Object data) {
+        int num = node.jjtGetNumChildren();
+        builder.append("{ ");
+        accept(node.jjtGetChild(0), data);
+        for (int i = 1; i < num; ++i) {
+            builder.append(", ");
+            accept(node.jjtGetChild(i), data);
+        }
+        builder.append(" }");
+        return data;
+    }
+
+    /** {@inheritDoc} */
+     public Object visit(ASTConstructorNode node, Object data) {
+        int num = node.jjtGetNumChildren();
+        builder.append("new ");
+        builder.append("(");
+        accept(node.jjtGetChild(0), data);
+        for (int i = 1; i < num; ++i) {
+            builder.append(", ");
+            accept(node.jjtGetChild(i), data);
+        }
+        builder.append(")");
+        return data;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTFunctionNode node, Object data) {
+        int num = node.jjtGetNumChildren();
+        accept(node.jjtGetChild(0), data);
+        builder.append(":");
+        accept(node.jjtGetChild(1), data);
+        builder.append("(");
+        for (int i = 2; i < num; ++i) {
+            if (i > 2) {
+                builder.append(", ");
+            }
+            accept(node.jjtGetChild(i), data);
+        }
+        builder.append(")");
+        return data;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTMethodNode node, Object data) {
+        int num = node.jjtGetNumChildren();
+        accept(node.jjtGetChild(0), data);
+        builder.append("(");
+        for (int i = 1; i < num; ++i) {
+            if (i > 1) {
+                builder.append(", ");
+            }
+            accept(node.jjtGetChild(i), data);
+        }
+        builder.append(")");
+        return data;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTModNode node, Object data) {
+        return infixChildren(node, " % ", false, data);
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTMulNode node, Object data) {
+        return infixChildren(node, " * ", false, data);
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTNENode node, Object data) {
+        return infixChildren(node, " != ", false, data);
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTNRNode node, Object data) {
+        return infixChildren(node, " !~ ", false, data);
+    }
+    
+    /** {@inheritDoc} */
+    public Object visit(ASTNotNode node, Object data) {
+        builder.append("!");
+        accept(node.jjtGetChild(0), data);
+        return data;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTNullLiteral node, Object data) {
+        check(node, "null", data);
+        return data;
+    }
+
+    /** {@inheritDoc} */
+   public Object visit(ASTOrNode node, Object data) {
+        // need parenthesis if not in operator precedence order
+        boolean paren = node.jjtGetParent() instanceof ASTAndNode;
+        return infixChildren(node, " || ", paren, data);
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTReference node, Object data) {
+        int num = node.jjtGetNumChildren();
+        accept(node.jjtGetChild(0), data);
+        for (int i = 1; i < num; ++i) {
+            builder.append(".");
+            accept(node.jjtGetChild(i), data);
+        }
+        return data;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTSizeFunction node, Object data) {
+        builder.append("size(");
+        accept(node.jjtGetChild(0), data);
+        builder.append(")");
+        return data;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTSizeMethod node, Object data) {
+        check(node, "size()", data);
+        return data;
+    }
+    
+    /** {@inheritDoc} */
+    public Object visit(ASTStringLiteral node, Object data) {
+        String img = node.image.replace("'", "\\'");
+        return check(node, "'" + img + "'", data);
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTTernaryNode node, Object data) {
+        accept(node.jjtGetChild(0), data);
+        if (node.jjtGetNumChildren() > 2) {
+            builder.append("? ");
+            accept(node.jjtGetChild(1), data);
+            builder.append(" : ");
+            accept(node.jjtGetChild(2), data);
+        } else {
+            builder.append("?:");
+            accept(node.jjtGetChild(1), data);
+
+        }
+        return data;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTTrueNode node, Object data) {
+        check(node, "true", data);
+        return data;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTUnaryMinusNode node, Object data) {
+        return prefixChild(node, "-", data);
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTWhileStatement node, Object data) {
+        builder.append("while (");
+        accept(node.jjtGetChild(0), data);
+        builder.append(") ");
+        if (node.jjtGetNumChildren() > 1) {
+            acceptStatement(node.jjtGetChild(1), data);
+        } else {
+            builder.append(';');
+        }
+        return data;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(SimpleNode node, Object data) {
+        throw new UnsupportedOperationException("unexpected type of node");
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTAmbiguous node, Object data) {
+        throw new UnsupportedOperationException("unexpected type of node");
+    }
+}
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/Expression.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/Expression.java
new file mode 100644
index 0000000..3966d77
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/Expression.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2;
+
+
+/**
+ * Represents a single JEXL expression.
+ * <p>
+ * This simple interface provides access to the underlying expression through
+ * {@link Expression#getExpression()}.
+ * </p>
+ *
+ * <p>
+ * An expression is different than a script - it is simply a reference of
+ * an expression.
+ * </p>
+ *
+ * @since 1.0
+ * @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
+ * @version $Id$
+ */
+public interface Expression {
+    /**
+     * Evaluates the expression with the variables contained in the
+     * supplied {@link JexlContext}.
+     *
+     * @param context A JexlContext containing variables.
+     * @return The result of this evaluation
+     * @throws JexlException on any error
+     */
+    Object evaluate(JexlContext context);
+
+    /**
+     * Returns the JEXL expression this Expression was created with.
+     *
+     * @return The JEXL expression to be evaluated
+     */
+    String getExpression();
+
+    /**
+     * Returns the JEXL expression by reconstructing it from the parsed tree.
+     * @return the JEXL expression
+     */
+    String dump();
+}
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/ExpressionImpl.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/ExpressionImpl.java
new file mode 100644
index 0000000..63f653e
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/ExpressionImpl.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2;
+
+import org.apache.commons.jexl2.parser.ASTJexlScript;
+
+/**
+ * Instances of ExpressionImpl are created by the {@link JexlEngine},
+ * and this is the default implementation of the {@link Expression} and
+ * {@link Script} interface.
+ * @since 1.0
+ * @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
+ * @version $Id$
+ */
+public class ExpressionImpl implements Expression, Script {
+    /** The engine for this expression. */
+    protected final JexlEngine jexl;
+    /**
+     * Original expression stripped from leading & trailing spaces.
+     */
+    protected final String expression;
+    /**
+     * The resulting AST we can interpret.
+     */
+    protected final ASTJexlScript script;
+
+
+    /**
+     * Do not let this be generally instantiated with a 'new'.
+     *
+     * @param engine the interpreter to evaluate the expression
+     * @param expr the expression.
+     * @param ref the parsed expression.
+     */
+    protected ExpressionImpl(JexlEngine engine, String expr, ASTJexlScript ref) {
+        jexl = engine;
+        expression = expr;
+        script = ref;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Object evaluate(JexlContext context) {
+        if (script.jjtGetNumChildren() < 1) {
+            return null;
+        }
+        Interpreter interpreter = jexl.createInterpreter(context);
+        return interpreter.interpret(script.jjtGetChild(0));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String dump() {
+        Debugger debug = new Debugger();
+        return debug.debug(script)? debug.toString() : "/*?*/";
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getExpression() {
+        return expression;
+    }
+
+    /**
+     * Provide a string representation of the expression.
+     *
+     * @return the expression or blank if it's null.
+     */
+    @Override
+    public String toString() {
+        String expr = getExpression();
+        return expr == null ? "" : expr;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getText() {
+        return toString();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Object execute(JexlContext context) {
+        Interpreter interpreter = jexl.createInterpreter(context);
+        return interpreter.interpret(script);
+    }
+
+}
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/Interpreter.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/Interpreter.java
new file mode 100644
index 0000000..d82f2bd
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/Interpreter.java
@@ -0,0 +1,1349 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.commons.jexl2.parser.SimpleNode;
+import org.apache.commons.logging.Log;
+
+import org.apache.commons.jexl2.parser.JexlNode;
+import org.apache.commons.jexl2.parser.ASTAdditiveNode;
+import org.apache.commons.jexl2.parser.ASTAdditiveOperator;
+import org.apache.commons.jexl2.parser.ASTAndNode;
+import org.apache.commons.jexl2.parser.ASTAmbiguous;
+import org.apache.commons.jexl2.parser.ASTArrayAccess;
+import org.apache.commons.jexl2.parser.ASTArrayLiteral;
+import org.apache.commons.jexl2.parser.ASTAssignment;
+import org.apache.commons.jexl2.parser.ASTBitwiseAndNode;
+import org.apache.commons.jexl2.parser.ASTBitwiseComplNode;
+import org.apache.commons.jexl2.parser.ASTBitwiseOrNode;
+import org.apache.commons.jexl2.parser.ASTBitwiseXorNode;
+import org.apache.commons.jexl2.parser.ASTBlock;
+import org.apache.commons.jexl2.parser.ASTConstructorNode;
+import org.apache.commons.jexl2.parser.ASTDivNode;
+import org.apache.commons.jexl2.parser.ASTEQNode;
+import org.apache.commons.jexl2.parser.ASTERNode;
+import org.apache.commons.jexl2.parser.ASTEmptyFunction;
+import org.apache.commons.jexl2.parser.ASTFalseNode;
+import org.apache.commons.jexl2.parser.ASTFunctionNode;
+import org.apache.commons.jexl2.parser.ASTFloatLiteral;
+import org.apache.commons.jexl2.parser.ASTForeachStatement;
+import org.apache.commons.jexl2.parser.ASTGENode;
+import org.apache.commons.jexl2.parser.ASTGTNode;
+import org.apache.commons.jexl2.parser.ASTIdentifier;
+import org.apache.commons.jexl2.parser.ASTIfStatement;
+import org.apache.commons.jexl2.parser.ASTIntegerLiteral;
+import org.apache.commons.jexl2.parser.ASTJexlScript;
+import org.apache.commons.jexl2.parser.ASTLENode;
+import org.apache.commons.jexl2.parser.ASTLTNode;
+import org.apache.commons.jexl2.parser.ASTMapEntry;
+import org.apache.commons.jexl2.parser.ASTMapLiteral;
+import org.apache.commons.jexl2.parser.ASTMethodNode;
+import org.apache.commons.jexl2.parser.ASTModNode;
+import org.apache.commons.jexl2.parser.ASTMulNode;
+import org.apache.commons.jexl2.parser.ASTNENode;
+import org.apache.commons.jexl2.parser.ASTNRNode;
+import org.apache.commons.jexl2.parser.ASTNotNode;
+import org.apache.commons.jexl2.parser.ASTNullLiteral;
+import org.apache.commons.jexl2.parser.ASTOrNode;
+import org.apache.commons.jexl2.parser.ASTReference;
+import org.apache.commons.jexl2.parser.ASTSizeFunction;
+import org.apache.commons.jexl2.parser.ASTSizeMethod;
+import org.apache.commons.jexl2.parser.ASTStringLiteral;
+import org.apache.commons.jexl2.parser.ASTTernaryNode;
+import org.apache.commons.jexl2.parser.ASTTrueNode;
+import org.apache.commons.jexl2.parser.ASTUnaryMinusNode;
+import org.apache.commons.jexl2.parser.ASTWhileStatement;
+import org.apache.commons.jexl2.parser.Node;
+import org.apache.commons.jexl2.parser.ParserVisitor;
+
+import org.apache.commons.jexl2.introspection.Uberspect;
+import org.apache.commons.jexl2.introspection.JexlMethod;
+import org.apache.commons.jexl2.introspection.JexlPropertyGet;
+import org.apache.commons.jexl2.introspection.JexlPropertySet;
+
+/**
+ * An interpreter of JEXL syntax.
+ *
+ * @since 2.0
+ */
+public class Interpreter implements ParserVisitor {
+    /** The logger. */
+    protected final Log logger;
+    /** The uberspect. */
+    protected final Uberspect uberspect;
+    /** The arithmetic handler. */
+    protected final JexlArithmetic arithmetic;
+    /** The map of registered functions. */
+    protected final Map<String, Object> functions;
+    /** The map of registered functions. */
+    protected Map<String, Object> functors;
+    /** The context to store/retrieve variables. */
+    protected final JexlContext context;
+    /** Strict interpreter flag. */
+    protected final boolean strict;
+    /** Silent intepreter flag. */
+    protected boolean silent;
+    /** Cache executors. */
+    protected final boolean  cache;
+    /** Registers made of 2 pairs of {register-name, value}. */
+    protected Object[] registers = null;
+    /** Empty parameters for method matching. */
+    protected static final Object[] EMPTY_PARAMS = new Object[0];
+
+    /**
+     * Creates an interpreter.
+     * @param jexl the engine creating this interpreter
+     * @param aContext the context to evaluate expression
+     */
+    public Interpreter(JexlEngine jexl, JexlContext aContext) {
+        this.logger = jexl.logger;
+        this.uberspect = jexl.uberspect;
+        this.arithmetic = jexl.arithmetic;
+        this.functions = jexl.functions;
+        this.strict = !this.arithmetic.isLenient();
+        this.silent = jexl.silent;
+        this.cache = jexl.cache != null;
+        this.context = aContext;
+        this.functors = null;
+    }
+
+    /**
+     * Sets whether this interpreter throws JexlException during evaluation.
+     * @param flag true means no JexlException will be thrown but will be logged
+     *        as info through the Jexl engine logger, false allows them to be thrown.
+     */
+    public void setSilent(boolean flag) {
+        this.silent = flag;
+    }
+
+    /**
+     * Checks whether this interpreter throws JexlException during evaluation.
+     * @return true if silent, false otherwise
+     */
+    public boolean isSilent() {
+        return this.silent;
+    }
+
+    /**
+     * Interpret the given script/expression.
+     * <p>
+     * If the underlying JEXL engine is silent, errors will be logged through its logger as info.
+     * </p>
+     * @param node the script or expression to interpret.
+     * @return the result of the interpretation.
+     * @throws JexlException if any error occurs during interpretation.
+     */
+    public Object interpret(JexlNode node) {
+        try {
+            return node.jjtAccept(this, null);
+        } catch (JexlException xjexl) {
+            if (silent) {
+                logger.warn(xjexl.getMessage(), xjexl.getCause());
+                return null;
+            }
+            throw xjexl;
+        }
+    }
+
+    /**
+     * Gets the uberspect.
+     * @return an {@link Uberspect}
+     */
+    protected Uberspect getUberspect() {
+        return uberspect;
+    }
+
+    /**
+     * Sets this interpreter registers for bean access/assign expressions.
+     * @param theRegisters the array of registers
+     */
+    protected void setRegisters(Object[] theRegisters) {
+        this.registers = theRegisters;
+    }
+
+    /**
+     * Finds the node causing a NPE for diadic operators.
+     * @param xrt the RuntimeException
+     * @param node the parent node
+     * @param left the left argument
+     * @param right the right argument
+     * @return the left, right or parent node
+     */
+    protected JexlNode findNullOperand(RuntimeException xrt, JexlNode node, Object left, Object right) {
+        if (xrt instanceof NullPointerException
+                && JexlException.NULL_OPERAND == xrt.getMessage()) {
+            if (left == null) {
+                return node.jjtGetChild(0);
+            }
+            if (right == null) {
+                return node.jjtGetChild(1);
+            }
+        }
+        return node;
+    }
+
+    /**
+     * Triggered when variable can not be resolved.
+     * @param xjexl the JexlException ("undefined variable " + variable)
+     * @return throws JexlException if strict, null otherwise
+     */
+    protected Object unknownVariable(JexlException xjexl) {
+        if (strict) {
+            throw xjexl;
+        }
+        if (!silent) {
+            logger.warn(xjexl.getMessage());
+        }
+        return null;
+    }
+
+    /**
+     * Triggered when method, function or constructor invocation fails.
+     * @param xjexl the JexlException wrapping the original error
+     * @return throws JexlException if strict, null otherwise
+     */
+    protected Object invocationFailed(JexlException xjexl) {
+        if (strict) {
+            throw xjexl;
+        }
+        if (!silent) {
+            logger.warn(xjexl.getMessage(), xjexl.getCause());
+        }
+        return null;
+    }
+
+    /**
+     * Resolves a namespace, eventually allocating an instance using context as constructor argument.
+     * The lifetime of such instances span the current expression or script evaluation.
+     *
+     * @param prefix the prefix name (may be null for global namespace)
+     * @param node the AST node
+     * @return the namespace instance
+     */
+    protected Object resolveNamespace(String prefix, JexlNode node) {
+        Object namespace;
+        // check whether this namespace is a functor
+        if (functors != null) {
+            namespace = functors.get(prefix);
+            if (namespace != null) {
+                return namespace;
+            }
+        }
+        namespace = functions.get(prefix);
+        if (namespace == null) {
+            throw new JexlException(node, "no such function namespace " + prefix);
+        }
+        // allow namespace to be instantiated as functor with context
+        if (namespace instanceof Class<?>) {
+            Object[] args = new Object[]{context};
+            Constructor<?> ctor = uberspect.getConstructor(namespace,args, node);
+            if (ctor != null) {
+                try {
+                    namespace = ctor.newInstance(args);
+                    if (functors == null) {
+                        functors = new HashMap<String, Object>();
+                    }
+                    functors.put(prefix, namespace);
+                } catch (Exception xinst) {
+                    throw new JexlException(node, "unable to instantiate namespace " + prefix, xinst);
+                }
+            }
+        }
+        return namespace;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTAdditiveNode node, Object data) {
+        /**
+         * The pattern for exception mgmt is to let the child*.jjtAccept
+         * out of the try/catch loop so that if one fails, the ex will
+         * traverse up to the interpreter.
+         * In cases where this is not convenient/possible, JexlException must
+         * be caught explicitly and rethrown.
+         */
+        Object left = node.jjtGetChild(0).jjtAccept(this, data);
+        for(int c = 2, size = node.jjtGetNumChildren(); c < size; c += 2) {
+            Object right = node.jjtGetChild(c).jjtAccept(this, data);
+            try {
+                JexlNode op = node.jjtGetChild(c - 1);
+                if (op instanceof ASTAdditiveOperator) {
+                    String which = ((ASTAdditiveOperator) op).image;
+                    if ("+".equals(which)) {
+                        left = arithmetic.add(left, right);
+                        continue;
+                    }
+                    if ("-".equals(which)) {
+                        left = arithmetic.subtract(left, right);
+                        continue;
+                    }
+                    throw new UnsupportedOperationException("unknown operator " + which);
+                }
+                throw new IllegalArgumentException("unknown operator " + op);
+            } catch (RuntimeException xrt) {
+                JexlNode xnode = findNullOperand(xrt, node, left, right);
+                throw new JexlException(xnode, "+/- error", xrt);
+            }
+        }
+        return left;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTAdditiveOperator node, Object data) {
+        throw new UnsupportedOperationException("Shoud not be called.");
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTAndNode node, Object data) {
+        Object left = node.jjtGetChild(0).jjtAccept(this, data);
+        try {
+            boolean leftValue = arithmetic.toBoolean(left);
+            if (!leftValue) {
+                return Boolean.FALSE;
+            }
+        } catch (RuntimeException xrt) {
+            throw new JexlException(node.jjtGetChild(0), "boolean coercion error", xrt);
+        }
+        Object right = node.jjtGetChild(1).jjtAccept(this, data);
+        try {
+            boolean rightValue = arithmetic.toBoolean(right);
+            if (!rightValue) {
+                return Boolean.FALSE;
+            }
+        } catch (RuntimeException xrt) {
+            throw new JexlException(node.jjtGetChild(1), "boolean coercion error", xrt);
+        }
+        return Boolean.TRUE;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTArrayAccess node, Object data) {
+        // first objectNode is the identifier
+        Object object = node.jjtGetChild(0).jjtAccept(this, data);
+        // can have multiple nodes - either an expression, integer literal or
+        // reference
+        int numChildren = node.jjtGetNumChildren();
+        for (int i = 1; i < numChildren; i++) {
+            JexlNode nindex = node.jjtGetChild(i);
+            Object index = nindex.jjtAccept(this, null);
+            object = getAttribute(object, index, nindex);
+        }
+
+        return object;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTArrayLiteral node, Object data) {
+        int childCount = node.jjtGetNumChildren();
+        Object[] array = new Object[childCount];
+        for (int i = 0; i < childCount; i++) {
+            Object entry = node.jjtGetChild(i).jjtAccept(this, data);
+            array[i] = entry;
+        }
+        return arithmetic.narrowArrayType(array);
+    }
+    
+    /** {@inheritDoc} */
+    public Object visit(ASTAssignment node, Object data) {
+        // left contains the reference to assign to
+        JexlNode left = node.jjtGetChild(0);
+        if (!(left instanceof ASTReference)) {
+            throw new JexlException(left, "illegal assignment form");
+        }
+        // right is the value expression to assign
+        Object right = node.jjtGetChild(1).jjtAccept(this, data);
+
+        // determine initial object & property:
+        JexlNode objectNode = null;
+        Object object = null;
+        JexlNode propertyNode = null;
+        Object property = null;
+        boolean isVariable = true;
+        int v = 0;
+        StringBuilder variableName = null;
+        // 1: follow children till penultimate
+        int last = left.jjtGetNumChildren() - 1;
+        for (int c = 0; c < last; ++c) {
+            objectNode = left.jjtGetChild(c);
+            // evaluate the property within the object
+            object = objectNode.jjtAccept(this, object);
+            if (object != null) {
+                continue;
+            }
+            isVariable &= objectNode instanceof ASTIdentifier;
+            // if we get null back as a result, check for an ant variable
+            if (isVariable) {
+                if (v == 0) {
+                    variableName = new StringBuilder(left.jjtGetChild(0).image);
+                    v = 1;
+                }
+                for(; v <= c; ++v) {
+                    variableName.append('.');
+                    variableName.append(left.jjtGetChild(v).image);
+                }
+                object = context.get(variableName.toString());
+                // disallow mixing ant & bean with same root; avoid ambiguity
+                if (object != null) {
+                    isVariable = false;
+                }
+            } else {
+                throw new JexlException(objectNode, "illegal assignment form");
+            }
+        }
+        // 2: last objectNode will perform assignement in all cases
+        propertyNode = left.jjtGetChild(last);
+        if (propertyNode instanceof ASTIdentifier) {
+            property = ((ASTIdentifier) propertyNode).image;
+            // deal with ant variable
+            if (isVariable && object == null) {
+                if (variableName != null) {
+                    if (last > 0) {
+                        variableName.append('.');
+                    }
+                    variableName.append(property);
+                    property = variableName.toString();
+                }
+                context.set(String.valueOf(property), right);
+                return right;
+            }
+        } else if (propertyNode instanceof ASTIntegerLiteral) {
+            property = visit((ASTIntegerLiteral) propertyNode, null);
+            // deal with ant variable
+            if (isVariable && object == null) {
+                if (variableName != null) {
+                    if (last > 0) {
+                        variableName.append('.');
+                    }
+                    variableName.append(property);
+                    property = variableName.toString();
+                }
+                context.set(String.valueOf(property), right);
+                return right;
+            }
+        } else if (propertyNode instanceof ASTArrayAccess) {
+            // first objectNode is the identifier
+            objectNode = propertyNode;
+            ASTArrayAccess narray = (ASTArrayAccess) objectNode;
+            Object nobject = narray.jjtGetChild(0).jjtAccept(this, object);
+            if (nobject == null) {
+                throw new JexlException(objectNode, "array element is null");
+            } else {
+                object = nobject;
+            }
+            // can have multiple nodes - either an expression, integer literal or
+            // reference
+            last = narray.jjtGetNumChildren() - 1;
+            for (int i = 1; i < last; i++) {
+                objectNode = narray.jjtGetChild(i);
+                Object index = objectNode.jjtAccept(this, null);
+                object = getAttribute(object, index, objectNode);
+            }
+            property = narray.jjtGetChild(last).jjtAccept(this, null);
+        } else {
+            throw new JexlException(objectNode, "illegal assignment form");
+        }
+        if (property == null) {
+            // no property, we fail
+            throw new JexlException(propertyNode, "property is null");
+        }
+        if (object == null) {
+            // no object, we fail
+            throw new JexlException(objectNode, "bean is null");
+        }
+        // one before last, assign
+        setAttribute(object, property, right, propertyNode);
+        return right;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTBitwiseAndNode node, Object data) {
+        Object left = node.jjtGetChild(0).jjtAccept(this, data);
+        Object right = node.jjtGetChild(1).jjtAccept(this, data);
+        int n = 0;
+        // coerce these two values longs and 'and'.
+        try {
+            long l = arithmetic.toLong(left);
+            n = 1;
+            long r = arithmetic.toLong(right);
+            return Long.valueOf(l & r);
+        } catch (RuntimeException xrt) {
+            throw new JexlException(node.jjtGetChild(n), "long coercion error", xrt);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTBitwiseComplNode node, Object data) {
+        Object left = node.jjtGetChild(0).jjtAccept(this, data);
+        try {
+            long l = arithmetic.toLong(left);
+            return Long.valueOf(~l);
+        } catch (RuntimeException xrt) {
+            throw new JexlException(node.jjtGetChild(0), "long coercion error", xrt);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTBitwiseOrNode node, Object data) {
+        Object left = node.jjtGetChild(0).jjtAccept(this, data);
+        Object right = node.jjtGetChild(1).jjtAccept(this, data);
+        int n = 0;
+        // coerce these two values longs and 'or'.
+        try {
+            long l = arithmetic.toLong(left);
+            n = 1;
+            long r = arithmetic.toLong(right);
+            return Long.valueOf(l | r);
+        } catch (RuntimeException xrt) {
+            throw new JexlException(node.jjtGetChild(n), "long coercion error", xrt);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTBitwiseXorNode node, Object data) {
+        Object left = node.jjtGetChild(0).jjtAccept(this, data);
+        Object right = node.jjtGetChild(1).jjtAccept(this, data);
+        int n = 0;
+        // coerce these two values longs and 'xor'.
+        try {
+            long l = arithmetic.toLong(left);
+            n = 1;
+            long r = arithmetic.toLong(right);
+            return Long.valueOf(l ^ r);
+        } catch (RuntimeException xrt) {
+            throw new JexlException(node.jjtGetChild(n), "long coercion error", xrt);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTBlock node, Object data) {
+        int numChildren = node.jjtGetNumChildren();
+        Object result = null;
+        for (int i = 0; i < numChildren; i++) {
+            result = node.jjtGetChild(i).jjtAccept(this, data);
+        }
+        return result;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTDivNode node, Object data) {
+        Object left = node.jjtGetChild(0).jjtAccept(this, data);
+        Object right = node.jjtGetChild(1).jjtAccept(this, data);
+        try {
+            return arithmetic.divide(left, right);
+        } catch (RuntimeException xrt) {
+            if (!strict && xrt instanceof ArithmeticException) {
+                return new Double(0.0);
+            }
+            JexlNode xnode = findNullOperand(xrt, node, left, right);
+            throw new JexlException(xnode, "divide error", xrt);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTEmptyFunction node, Object data) {
+        Object o = node.jjtGetChild(0).jjtAccept(this, data);
+        if (o == null) {
+            return Boolean.TRUE;
+        }
+        if (o instanceof String && "".equals(o)) {
+            return Boolean.TRUE;
+        }
+        if (o.getClass().isArray() && ((Object[]) o).length == 0) {
+            return Boolean.TRUE;
+        }
+        if (o instanceof Collection<?> && ((Collection<?>) o).isEmpty()) {
+            return Boolean.TRUE;
+        }
+        // Map isn't a collection
+        if (o instanceof Map<?, ?> && ((Map<?, ?>) o).isEmpty()) {
+            return Boolean.TRUE;
+        }
+        return Boolean.FALSE;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTEQNode node, Object data) {
+        Object left = node.jjtGetChild(0).jjtAccept(this, data);
+        Object right = node.jjtGetChild(1).jjtAccept(this, data);
+        try {
+            return arithmetic.equals(left, right) ? Boolean.TRUE : Boolean.FALSE;
+        } catch (RuntimeException xrt) {
+            throw new JexlException(node, "== error", xrt);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTFalseNode node, Object data) {
+        return Boolean.FALSE;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTFloatLiteral node, Object data) {
+        Float value = (Float) node.jjtGetValue();
+        if (value == null) {
+            value = Float.valueOf(node.image);
+            node.jjtSetValue(value);
+        }
+        return value;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTForeachStatement node, Object data) {
+        Object result = null;
+        /* first objectNode is the loop variable */
+        ASTReference loopReference = (ASTReference) node.jjtGetChild(0);
+        ASTIdentifier loopVariable = (ASTIdentifier) loopReference.jjtGetChild(0);
+        /* second objectNode is the variable to iterate */
+        Object iterableValue = node.jjtGetChild(1).jjtAccept(this, data);
+        // make sure there is a value to iterate on and a statement to execute
+        if (iterableValue != null && node.jjtGetNumChildren() >= 3) {
+            /* third objectNode is the statement to execute */
+            JexlNode statement = node.jjtGetChild(2);
+            // get an iterator for the collection/array etc via the
+            // introspector.
+            Iterator<?> itemsIterator = getUberspect().getIterator(iterableValue, node);
+            if (itemsIterator != null) {
+                while (itemsIterator.hasNext()) {
+                    // set loopVariable to value of iterator
+                    Object value = itemsIterator.next();
+                    context.set(loopVariable.image, value);
+                    // execute statement
+                    result = statement.jjtAccept(this, data);
+                }
+            }
+        }
+        return result;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTGENode node, Object data) {
+        Object left = node.jjtGetChild(0).jjtAccept(this, data);
+        Object right = node.jjtGetChild(1).jjtAccept(this, data);
+        try {
+            return arithmetic.greaterThanOrEqual(left, right) ? Boolean.TRUE : Boolean.FALSE;
+        } catch (RuntimeException xrt) {
+            throw new JexlException(node, ">= error", xrt);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTGTNode node, Object data) {
+        Object left = node.jjtGetChild(0).jjtAccept(this, data);
+        Object right = node.jjtGetChild(1).jjtAccept(this, data);
+        try {
+            return arithmetic.greaterThan(left, right) ? Boolean.TRUE : Boolean.FALSE;
+        } catch (RuntimeException xrt) {
+            throw new JexlException(node, "> error", xrt);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTERNode node, Object data) {
+        Object left = node.jjtGetChild(0).jjtAccept(this, data);
+        Object right = node.jjtGetChild(1).jjtAccept(this, data);
+        try {
+            return arithmetic.matches(left, right) ? Boolean.TRUE : Boolean.FALSE;
+        } catch (RuntimeException xrt) {
+            throw new JexlException(node, "=~ error", xrt);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTIdentifier node, Object data) {
+        String name = node.image;
+        if (data == null) {
+            if (registers != null) {
+                if (registers[0].equals(name)) {
+                    return registers[1];
+                }
+                if (registers[2].equals(name)) {
+                    return registers[3];
+                }
+            }
+            Object value = context.get(name);
+            if (value == null
+                && !(node.jjtGetParent() instanceof ASTReference)
+                && !context.has(name)) {
+                JexlException xjexl = new JexlException(node, "undefined variable " + name);
+                return unknownVariable(xjexl);
+            }
+            return value;
+        } else {
+            return getAttribute(data, name, node);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTIfStatement node, Object data) {
+        int n = 0;
+        try {
+            Object result = null;
+            /* first objectNode is the expression */
+            Object expression = node.jjtGetChild(0).jjtAccept(this, data);
+            if (arithmetic.toBoolean(expression)) {
+                // first objectNode is true statement
+                n = 1;
+                result = node.jjtGetChild(1).jjtAccept(this, data);
+            } else {
+                // if there is a false, execute it. false statement is the second
+                // objectNode
+                if (node.jjtGetNumChildren() == 3) {
+                    n = 2;
+                    result = node.jjtGetChild(2).jjtAccept(this, data);
+                }
+            }
+            return result;
+        } catch (JexlException error) {
+            throw error;
+        } catch (RuntimeException xrt) {
+            throw new JexlException(node.jjtGetChild(n), "if error", xrt);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTIntegerLiteral node, Object data) {
+        if (data != null) {
+            Integer value = Integer.valueOf(node.image);
+            return getAttribute(data, value, node);
+        }
+        Integer value = (Integer) node.jjtGetValue();
+        if (value == null) {
+            value = Integer.valueOf(node.image);
+            node.jjtSetValue(value);
+        }
+        return value;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTJexlScript node, Object data) {
+        int numChildren = node.jjtGetNumChildren();
+        Object result = null;
+        for (int i = 0; i < numChildren; i++) {
+            JexlNode child = node.jjtGetChild(i);
+            result = child.jjtAccept(this, data);
+        }
+        return result;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTLENode node, Object data) {
+        Object left = node.jjtGetChild(0).jjtAccept(this, data);
+        Object right = node.jjtGetChild(1).jjtAccept(this, data);
+        try {
+            return arithmetic.lessThanOrEqual(left, right) ? Boolean.TRUE : Boolean.FALSE;
+        } catch (RuntimeException xrt) {
+            throw new JexlException(node, "<= error", xrt);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTLTNode node, Object data) {
+        Object left = node.jjtGetChild(0).jjtAccept(this, data);
+        Object right = node.jjtGetChild(1).jjtAccept(this, data);
+        try {
+            return arithmetic.lessThan(left, right) ? Boolean.TRUE : Boolean.FALSE;
+        } catch (RuntimeException xrt) {
+            throw new JexlException(node, "< error", xrt);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTMapEntry node, Object data) {
+        Object key = node.jjtGetChild(0).jjtAccept(this, data);
+        Object value = node.jjtGetChild(1).jjtAccept(this, data);
+        return new Object[]{key, value};
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTMapLiteral node, Object data) {
+        int childCount = node.jjtGetNumChildren();
+        Map<Object, Object> map = new HashMap<Object, Object>();
+
+        for (int i = 0; i < childCount; i++) {
+            Object[] entry = (Object[]) (node.jjtGetChild(i)).jjtAccept(this, data);
+            map.put(entry[0], entry[1]);
+        }
+
+        return map;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTMethodNode node, Object data) {
+        // the object to invoke the method on should be in the data argument
+        if (data == null) {
+            // if the first child of the (ASTReference) parent,
+            // it is considered as calling a 'top level' function
+            if (node.jjtGetParent().jjtGetChild(0) == node) {
+                data = resolveNamespace(null, node);
+                if (data == null) {
+                    throw new JexlException(node, "no default function namespace");
+                }
+            } else {
+                throw new JexlException(node, "attempting to call method on null");
+            }
+        }
+        // objectNode 0 is the identifier (method name), the others are parameters.
+        String methodName = ((ASTIdentifier) node.jjtGetChild(0)).image;
+
+        // get our arguments
+        int argc = node.jjtGetNumChildren() - 1;
+        Object[] argv = new Object[argc];
+        for (int i = 0; i < argc; i++) {
+            argv[i] = node.jjtGetChild(i + 1).jjtAccept(this, null);
+        }
+
+        JexlException xjexl = null;
+        try {
+            // attempt to reuse last executor cached in volatile JexlNode.value
+            if (cache) {
+                Object cached = node.jjtGetValue();
+                if (cached instanceof JexlMethod) {
+                    JexlMethod me = (JexlMethod) cached;
+                    Object eval = me.tryInvoke(methodName, data, argv);
+                    if (!me.tryFailed(eval)) {
+                        return eval;
+                    }
+                }
+            }
+            JexlMethod vm = uberspect.getMethod(data, methodName, argv, node);
+            // DG: If we can't find an exact match, narrow the parameters and try again!
+            if (vm == null) {
+                if (arithmetic.narrowArguments(argv)) {
+                    vm = uberspect.getMethod(data, methodName, argv, node);
+                }
+                if (vm == null) {
+                    xjexl = new JexlException(node, "unknown or ambiguous method", null);
+                }
+            }
+            if (xjexl == null) {
+                Object eval = vm.invoke(data, argv); // vm cannot be null if xjexl is null
+                // cache executor in volatile JexlNode.value
+                if (cache && vm.isCacheable()) {
+                    node.jjtSetValue(vm);
+                }
+                return eval;
+            }
+        } catch (InvocationTargetException e) {
+            xjexl = new JexlException(node, "method invocation error", e.getCause());
+        } catch (Exception e) {
+            xjexl = new JexlException(node, "method error", e);
+        }
+        return invocationFailed(xjexl);
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTConstructorNode node, Object data) {
+        // first child is class or class name
+        Object cobject = node.jjtGetChild(0).jjtAccept(this, data);
+        // get the ctor args
+        int argc = node.jjtGetNumChildren() - 1;
+        Object[] argv = new Object[argc];
+        for (int i = 0; i < argc; i++) {
+            argv[i] = node.jjtGetChild(i + 1).jjtAccept(this, null);
+        }
+
+        JexlException xjexl = null;
+        try {
+            Constructor<?> ctor = uberspect.getConstructor(cobject, argv, node);
+            // DG: If we can't find an exact match, narrow the parameters and
+            // try again!
+            if (ctor == null) {
+                if (arithmetic.narrowArguments(argv)) {
+                    ctor = uberspect.getConstructor(cobject, argv, node);
+                }
+                if (ctor == null) {
+                    xjexl = new JexlException(node, "unknown constructor", null);
+                }
+            }
+            if (xjexl == null) {
+                return ctor.newInstance(argv);
+            }
+        } catch (InvocationTargetException e) {
+            xjexl = new JexlException(node, "constructor invocation error", e.getCause());
+        } catch (Exception e) {
+            xjexl = new JexlException(node, "constructor error", e);
+        }
+        return invocationFailed(xjexl);
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTFunctionNode node, Object data) {
+        // objectNode 0 is the prefix
+        String prefix = ((ASTIdentifier) node.jjtGetChild(0)).image;
+        Object namespace = resolveNamespace(prefix, node);
+        // objectNode 1 is the identifier , the others are parameters.
+        String function = ((ASTIdentifier) node.jjtGetChild(1)).image;
+
+        // get our args
+        int argc = node.jjtGetNumChildren() - 2;
+        Object[] argv = new Object[argc];
+        for (int i = 0; i < argc; i++) {
+            argv[i] = node.jjtGetChild(i + 2).jjtAccept(this, null);
+        }
+
+        JexlException xjexl = null;
+        try {
+            // attempt to reuse last executor cached in volatile JexlNode.value
+            if (cache) {
+                Object cached = node.jjtGetValue();
+                if (cached instanceof JexlMethod) {
+                    JexlMethod me = (JexlMethod) cached;
+                    Object eval = me.tryInvoke(function, namespace, argv);
+                    if (!me.tryFailed(eval)) {
+                        return eval;
+                    }
+                }
+            }
+            JexlMethod vm = uberspect.getMethod(namespace, function, argv, node);
+            // DG: If we can't find an exact match, narrow the parameters and
+            // try again!
+            if (vm == null) {
+                // replace all numbers with the smallest type that will fit
+                if (arithmetic.narrowArguments(argv)) {
+                    vm = uberspect.getMethod(namespace, function, argv, node);
+                }
+                if (vm == null) {
+                    xjexl = new JexlException(node, "unknown function", null);
+                }
+            }
+            if (xjexl == null) {
+                Object eval = vm.invoke(namespace, argv); // vm cannot be null if xjexl is null
+                // cache executor in volatile JexlNode.value
+                if (cache && vm.isCacheable()) {
+                    node.jjtSetValue(vm);
+                }
+                return eval;
+            }
+        } catch (InvocationTargetException e) {
+            xjexl = new JexlException(node, "function invocation error", e.getCause());
+        } catch (Exception e) {
+            xjexl = new JexlException(node, "function error", e);
+        }
+        return invocationFailed(xjexl);
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTModNode node, Object data) {
+        Object left = node.jjtGetChild(0).jjtAccept(this, data);
+        Object right = node.jjtGetChild(1).jjtAccept(this, data);
+        try {
+            return arithmetic.mod(left, right);
+        } catch (RuntimeException xrt) {
+            if (!strict && xrt instanceof ArithmeticException) {
+                return new Double(0.0);
+            }
+            JexlNode xnode = findNullOperand(xrt, node, left, right);
+            throw new JexlException(xnode, "% error", xrt);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTMulNode node, Object data) {
+        Object left = node.jjtGetChild(0).jjtAccept(this, data);
+        Object right = node.jjtGetChild(1).jjtAccept(this, data);
+        try {
+            return arithmetic.multiply(left, right);
+        } catch (RuntimeException xrt) {
+            JexlNode xnode = findNullOperand(xrt, node, left, right);
+            throw new JexlException(xnode, "* error", xrt);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTNENode node, Object data) {
+        Object left = node.jjtGetChild(0).jjtAccept(this, data);
+        Object right = node.jjtGetChild(1).jjtAccept(this, data);
+        try {
+            return arithmetic.equals(left, right) ? Boolean.FALSE : Boolean.TRUE;
+        } catch (RuntimeException xrt) {
+            JexlNode xnode = findNullOperand(xrt, node, left, right);
+            throw new JexlException(xnode, "!= error", xrt);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTNRNode node, Object data) {
+        Object left = node.jjtGetChild(0).jjtAccept(this, data);
+        Object right = node.jjtGetChild(1).jjtAccept(this, data);
+        try {
+            return arithmetic.matches(left, right) ? Boolean.FALSE : Boolean.TRUE;
+        } catch (RuntimeException xrt) {
+            throw new JexlException(node, "!~ error", xrt);
+        }
+    }
+    
+    /** {@inheritDoc} */
+    public Object visit(ASTNotNode node, Object data) {
+        Object val = node.jjtGetChild(0).jjtAccept(this, data);
+        return arithmetic.toBoolean(val) ? Boolean.FALSE : Boolean.TRUE;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTNullLiteral node, Object data) {
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTOrNode node, Object data) {
+        Object left = node.jjtGetChild(0).jjtAccept(this, data);
+        try {
+            boolean leftValue = arithmetic.toBoolean(left);
+            if (leftValue) {
+                return Boolean.TRUE;
+            }
+        } catch (RuntimeException xrt) {
+            throw new JexlException(node.jjtGetChild(0), "boolean coercion error", xrt);
+        }
+        Object right = node.jjtGetChild(1).jjtAccept(this, data);
+        try {
+            boolean rightValue = arithmetic.toBoolean(right);
+            if (rightValue) {
+                return Boolean.TRUE;
+            }
+        } catch (RuntimeException xrt) {
+            throw new JexlException(node.jjtGetChild(1), "boolean coercion error", xrt);
+        }
+        return Boolean.FALSE;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTReference node, Object data) {
+        // could be array access, identifier or map literal
+        // followed by zero or more ("." and array access, method, size,
+        // identifier or integer literal)
+
+        int numChildren = node.jjtGetNumChildren();
+
+        // pass first piece of data in and loop through children
+        Object result = null;
+        StringBuilder variableName = null;
+        boolean isVariable = true;
+        int v = 0;
+        for (int c = 0; c < numChildren; c++) {
+            JexlNode theNode = node.jjtGetChild(c);
+            isVariable &= (theNode instanceof ASTIdentifier);
+            result = theNode.jjtAccept(this, result);
+            // if we get null back a result, check for an ant variable
+            if (result == null && isVariable) {
+                if (v == 0) {
+                    variableName = new StringBuilder(node.jjtGetChild(0).image);
+                    v = 1;
+                }
+                for(; v <= c; ++v) {
+                    variableName.append('.');
+                    variableName.append(node.jjtGetChild(v).image);
+                }
+                result = context.get(variableName.toString());
+            }
+        }
+        if (result == null) {
+            if (isVariable
+                && !(node.jjtGetParent() instanceof ASTTernaryNode)
+                && !context.has(variableName.toString())) {
+                JexlException xjexl = new JexlException(node, "undefined variable " + variableName.toString());
+                return unknownVariable(xjexl);
+            }
+        }
+        return result;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTSizeFunction node, Object data) {
+        Object val = node.jjtGetChild(0).jjtAccept(this, data);
+
+        if (val == null) {
+            throw new JexlException(node, "size() : argument is null", null);
+        }
+
+        return Integer.valueOf(sizeOf(node, val));
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTSizeMethod node, Object data) {
+        return Integer.valueOf(sizeOf(node, data));
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTStringLiteral node, Object data) {
+        return node.image;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTTernaryNode node, Object data) {
+        Object condition = node.jjtGetChild(0).jjtAccept(this, data);
+        if (node.jjtGetNumChildren() == 3) {
+            if (condition != null && arithmetic.toBoolean(condition)) {
+                return node.jjtGetChild(1).jjtAccept(this, data);
+            } else {
+                return node.jjtGetChild(2).jjtAccept(this, data);
+            }
+        }
+        if (condition != null && !Boolean.FALSE.equals(condition)) {
+            return condition;
+        } else {
+            return node.jjtGetChild(1).jjtAccept(this, data);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTTrueNode node, Object data) {
+        return Boolean.TRUE;
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTUnaryMinusNode node, Object data) {
+        JexlNode valNode = node.jjtGetChild(0);
+        Object val = valNode.jjtAccept(this, data);
+        if (val instanceof Byte) {
+            byte valueAsByte = ((Byte) val).byteValue();
+            return Byte.valueOf((byte) -valueAsByte);
+        } else if (val instanceof Short) {
+            short valueAsShort = ((Short) val).shortValue();
+            return Short.valueOf((short) -valueAsShort);
+        } else if (val instanceof Integer) {
+            int valueAsInt = ((Integer) val).intValue();
+            return Integer.valueOf(-valueAsInt);
+        } else if (val instanceof Long) {
+            long valueAsLong = ((Long) val).longValue();
+            return Long.valueOf(-valueAsLong);
+        } else if (val instanceof Float) {
+            float valueAsFloat = ((Float) val).floatValue();
+            return new Float(-valueAsFloat);
+        } else if (val instanceof Double) {
+            double valueAsDouble = ((Double) val).doubleValue();
+            return new Double(-valueAsDouble);
+        } else if (val instanceof BigDecimal) {
+            BigDecimal valueAsBigD = (BigDecimal) val;
+            return valueAsBigD.negate();
+        } else if (val instanceof BigInteger) {
+            BigInteger valueAsBigI = (BigInteger) val;
+            return valueAsBigI.negate();
+        }
+        throw new JexlException(valNode, "not a number");
+    }
+
+    /** {@inheritDoc} */
+    public Object visit(ASTWhileStatement node, Object data) {
+        Object result = null;
+        /* first objectNode is the expression */
+        Node expressionNode = node.jjtGetChild(0);
+        while (arithmetic.toBoolean(expressionNode.jjtAccept(this, data))) {
+            // execute statement
+            result = node.jjtGetChild(1).jjtAccept(this, data);
+        }
+
+        return result;
+    }
+
+    /**
+     * Calculate the <code>size</code> of various types: Collection, Array,
+     * Map, String, and anything that has a int size() method.
+     * @param node the node that gave the value to size
+     * @param val the object to get the size of.
+     * @return the size of val
+     */
+    private int sizeOf(JexlNode node, Object val) {
+        if (val instanceof Collection<?>) {
+            return ((Collection<?>) val).size();
+        } else if (val.getClass().isArray()) {
+            return Array.getLength(val);
+        } else if (val instanceof Map<?, ?>) {
+            return ((Map<?, ?>) val).size();
+        } else if (val instanceof String) {
+            return ((String) val).length();
+        } else {
+            // check if there is a size method on the object that returns an
+            // integer and if so, just use it
+            Object[] params = new Object[0];
+            JexlMethod vm = uberspect.getMethod(val, "size", EMPTY_PARAMS, node);
+            if (vm != null && vm.getReturnType() == Integer.TYPE) {
+                Integer result;
+                try {
+                    result = (Integer) vm.invoke(val, params);
+                } catch (Exception e) {
+                    throw new JexlException(node, "size() : error executing", e);
+                }
+                return result.intValue();
+            }
+            throw new JexlException(node, "size() : unsupported type : " + val.getClass(), null);
+        }
+    }
+
+    /**
+     * Gets an attribute of an object.
+     *
+     * @param object to retrieve value from
+     * @param attribute the attribute of the object, e.g. an index (1, 0, 2) or
+     *            key for a map
+     * @return the attribute value
+     */
+    public Object getAttribute(Object object, Object attribute) {
+        return getAttribute(object, attribute, null);
+    }
+
+    /**
+     * Gets an attribute of an object.
+     *
+     * @param object to retrieve value from
+     * @param attribute the attribute of the object, e.g. an index (1, 0, 2) or
+     *            key for a map
+     * @param node the node that evaluated as the object
+     * @return the attribute value
+     */
+    protected Object getAttribute(Object object, Object attribute, JexlNode node) {
+        if (object == null) {
+            throw new JexlException(node, "object is null");
+        }
+        // attempt to reuse last executor cached in volatile JexlNode.value
+        if (node != null && cache) {
+            Object cached = node.jjtGetValue();
+            if (cached instanceof JexlPropertyGet) {
+                JexlPropertyGet vg = (JexlPropertyGet) cached;
+                Object value = vg.tryInvoke(object, attribute);
+                if (!vg.tryFailed(value)) {
+                    return value;
+                }
+            }
+        }
+        JexlPropertyGet vg = uberspect.getPropertyGet(object, attribute, node);
+        if (vg != null) {
+            try {
+                Object value = vg.invoke(object);
+                // cache executor in volatile JexlNode.value
+                if (node != null && cache && vg.isCacheable()) {
+                    node.jjtSetValue(vg);
+                }
+                return value;
+            } catch (Exception xany) {
+                if (node == null) {
+                    throw new RuntimeException(xany);
+                } else {
+                    JexlException xjexl = new JexlException(node, "get object property error", xany);
+                    if (strict) {
+                        throw xjexl;
+                    }
+                    if (!silent) {
+                        logger.warn(xjexl.getMessage());
+                    }
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Sets an attribute of an object.
+     *
+     * @param object to set the value to
+     * @param attribute the attribute of the object, e.g. an index (1, 0, 2) or
+     *            key for a map
+     * @param value the value to assign to the object's attribute
+     */
+    public void setAttribute(Object object, Object attribute, Object value) {
+        setAttribute(object, attribute, value, null);
+    }
+
+    /**
+     * Sets an attribute of an object.
+     *
+     * @param object to set the value to
+     * @param attribute the attribute of the object, e.g. an index (1, 0, 2) or
+     *            key for a map
+     * @param value the value to assign to the object's attribute
+     * @param node the node that evaluated as the object
+     */
+    protected void setAttribute(Object object, Object attribute, Object value, JexlNode node) {
+        // attempt to reuse last executor cached in volatile JexlNode.value
+        if (node != null && cache) {
+            Object cached = node.jjtGetValue();
+            if (cached instanceof JexlPropertySet) {
+                JexlPropertySet setter = (JexlPropertySet) cached;
+                Object eval = setter.tryInvoke(object, attribute, value);
+                if (!setter.tryFailed(eval)) {
+                    return;
+                }
+            }
+        }
+        JexlException xjexl = null;
+        JexlPropertySet vs = uberspect.getPropertySet(object, attribute, value, node);
+        if (vs != null) {
+            try {
+                // cache executor in volatile JexlNode.value
+                vs.invoke(object, value);
+                if (node != null && cache && vs.isCacheable()) {
+                    node.jjtSetValue(vs);
+                }
+                return;
+            } catch (RuntimeException xrt) {
+                if (node == null) {
+                    throw xrt;
+                }
+                xjexl = new JexlException(node, "set object property error", xrt);
+            } catch (Exception xany) {
+                if (node == null) {
+                    throw new RuntimeException(xany);
+                }
+                xjexl = new JexlException(node, "set object property error", xany);
+            }
+        }
+        if (xjexl == null) {
+            String error = "unable to set object property"
+                           + ", class: " + object.getClass().getName()
+                           + ", property: " + attribute;
+            if (node == null) {
+                throw new UnsupportedOperationException(error);
+            }
+            xjexl = new JexlException(node, error, null);
+        }
+        if (strict) {
+            throw xjexl;
+        }
+        if (!silent) {
+            logger.warn(xjexl.getMessage());
+        }
+    }
+
+    /**
+     * Unused, satisfy ParserVisitor interface.
+     * @param node a node
+     * @param data the data
+     * @return does not return
+     */
+    public Object visit(SimpleNode node, Object data) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    /**
+     * Unused, should throw in Parser.
+     * @param node a node
+     * @param data the data
+     * @return does not return
+     */
+    public Object visit(ASTAmbiguous node, Object data) {
+        throw new UnsupportedOperationException("unexpected type of node");
+    }
+}
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/JexlArithmetic.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/JexlArithmetic.java
new file mode 100644
index 0000000..69e926f
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/JexlArithmetic.java
@@ -0,0 +1,848 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * Perform arithmetic.
+ * <p>
+ * All arithmetic operators (+, - , *, /, %) follow the same rules regarding their arguments.
+ * <ol>
+ * <li>If both are null, result is 0</li>
+ * <li>If either is a floating point number, coerce both to Double and perform operation</li>
+ * <li>If both are BigInteger, treat as BigInteger and perform operation</li>
+ * <li>If either is a BigDecimal, coerce both to BigDecimal and and perform operation</li>
+ * <li>Else treat as BigInteger, perform operation and attempt to narrow result:
+ * <ol>
+ * <li>if both arguments can be narrowed to Integer, narrow result to Integer</li>
+ * <li>if both arguments can be narrowed to Long, narrow result to Long</li>
+ * <li>Else return result as BigInteger</li>
+ * </ol>
+ * </li>
+ * </ol>
+ * </p>
+ * @since 2.0
+ */
+public class JexlArithmetic {
+    /** Double.MAX_VALUE as BigDecimal. */
+    protected static final BigDecimal BIGD_DOUBLE_MAX_VALUE = BigDecimal.valueOf(Double.MAX_VALUE);
+    /** Double.MIN_VALUE as BigDecimal. */
+    protected static final BigDecimal BIGD_DOUBLE_MIN_VALUE = BigDecimal.valueOf(Double.MIN_VALUE);
+    /** Long.MAX_VALUE as BigInteger. */
+    protected static final BigInteger BIGI_LONG_MAX_VALUE = BigInteger.valueOf(Long.MAX_VALUE);
+    /** Long.MIN_VALUE as BigInteger. */
+    protected static final BigInteger BIGI_LONG_MIN_VALUE = BigInteger.valueOf(Long.MIN_VALUE);
+    /** Whether this JexlArithmetic instance behaves in strict or lenient mode. */
+    private boolean strict;
+
+    /**
+     * Creates a JexlArithmetic.
+     * @param lenient whether this arithmetic is lenient or strict
+     */
+    public JexlArithmetic(boolean lenient) {
+        this.strict = !lenient;
+    }
+
+    /**
+     * Sets whether this JexlArithmetic instance triggers errors during evaluation when
+     * null is used as an operand.
+     * <p>This method is <em>not</em> thread safe; it may be called as an optional step by the JexlEngine
+     * in its initialization code before expression creation &amp; evaluation.</p>
+     * @see JexlEngine#setSilent
+     * @see JexlEngine#setDebug
+     * @param lenient true means no JexlException will occur, false allows them
+     */
+    void setLenient(boolean lenient) {
+        this.strict = !lenient;
+    }
+
+    /**
+     * Checks whether this JexlArithmetic instance triggers errors during evaluation
+     * when null is used as an operand.
+     * @return true if lenient, false if strict
+     */
+    public boolean isLenient() {
+        return !this.strict;
+    }
+
+    /**
+     * The result of +,/,-,*,% when both operands are null.
+     * @return null if strict, else Long(0)
+     */
+    protected Object controlNullNullOperands() {
+        return strict? null : Integer.valueOf(0);
+    }
+
+    /**
+     * Throw a NPE if arithmetic is strict.
+     */
+    protected void controlNullOperand() {
+        if (strict) {
+            throw new NullPointerException(JexlException.NULL_OPERAND);
+        }
+    }
+
+    /**
+     * Test if either left or right are either a Float or Double.
+     * @param left one object to test
+     * @param right the other
+     * @return the result of the test.
+     */
+    protected boolean isFloatingPointType(Object left, Object right) {
+        return left instanceof Float || left instanceof Double || right instanceof Float || right instanceof Double;
+    }
+
+    /**
+     * Test if the passed value is a floating point number, i.e. a float, double
+     * or string with ( "." | "E" | "e").
+     *
+     * @param val the object to be tested
+     * @return true if it is, false otherwise.
+     */
+    protected boolean isFloatingPointNumber(Object val) {
+        if (val instanceof Float || val instanceof Double) {
+            return true;
+        }
+        if (val instanceof String) {
+            String string = (String) val;
+            return string.indexOf('.') != -1 || string.indexOf('e') != -1 || string.indexOf('E') != -1;
+        }
+        return false;
+    }
+
+    /**
+     * Is Object a floating point number.
+     *
+     * @param o Object to be analyzed.
+     * @return true if it is a Float or a Double.
+     */
+    protected boolean isFloatingPoint(final Object o) {
+        return o instanceof Float || o instanceof Double;
+    }
+
+    /**
+     * Is Object a whole number.
+     *
+     * @param o Object to be analyzed.
+     * @return true if Integer, Long, Byte, Short or Character.
+     */
+    protected boolean isNumberable(final Object o) {
+        return o instanceof Integer
+            || o instanceof Long
+            || o instanceof Byte
+            || o instanceof Short
+            || o instanceof Character;
+    }
+
+    /**
+     * Given a BigInteger, narrow it to an Integer or Long if it fits and the arguments
+     * class allow it.
+     * <p>
+     * The rules are:
+     * if either arguments is a BigInteger, no narrowing will occur
+     * if either arguments is a Long, no narrowing to Integer will occur
+     * </p>
+     * @param lhs the left hand side operand that lead to the bigi result
+     * @param rhs the right hand side operand that lead to the bigi result
+     * @param bigi the BigInteger to narrow
+     * @return an Integer or Long if narrowing is possible, the original BigInteger otherwise
+     */
+    protected Number narrowBigInteger(Object lhs, Object rhs, BigInteger bigi) {
+        //coerce to long if possible
+        if (!(lhs instanceof BigInteger || rhs instanceof BigInteger)
+            && bigi.compareTo(BIGI_LONG_MAX_VALUE) <= 0
+            && bigi.compareTo(BIGI_LONG_MIN_VALUE) >= 0) {
+            // coerce to int if possible
+            long l = bigi.longValue();
+            // coerce to int when possible (int being so often used in method parms)
+            if (!(lhs instanceof Long || rhs instanceof Long)
+                && l <= Integer.MAX_VALUE
+                && l >= Integer.MIN_VALUE) {
+                return Integer.valueOf((int) l);
+            }
+            return Long.valueOf(l);
+        }
+        return bigi;
+    }
+
+    /**
+     * Given an array of objects, attempt to type it more strictly.
+     * <ul>
+     * <li>If all objects are of the same type, the array returned will be an array of that same type</li>
+     * <li>If all objects are Numbers, the array returned will be an array of Numbers</li>
+     * <li>If all objects are convertible to a primitive type, the array returned will be an array
+     * of the primitive type</li>
+     * </ul>
+     * @param untyped an untyped array
+     * @return the original array if the attempt to strictly type the array fails, a typed array otherwise
+     */
+    protected Object narrowArrayType(Object[] untyped) {
+        final int size = untyped.length;
+        Class<?> commonClass = null;
+        if (size > 0) {
+            // base common class on first entry
+            commonClass = untyped[0].getClass();
+            final boolean isNumber = Number.class.isAssignableFrom(commonClass);
+            // for all children after first...
+            for (int i = 1; i < size; i++) {
+                Class<?> eclass = untyped[i].getClass();
+                // detect same type for all elements in array
+                if (!Object.class.equals(commonClass) && !commonClass.equals(eclass)) {
+                    // if both are numbers...
+                    if (isNumber && Number.class.isAssignableFrom(eclass)) {
+                        commonClass = Number.class;
+                    } else {
+                        commonClass = Object.class;
+                    }
+                }
+            }
+            // convert array to the common class if not Object.class
+            if (!Object.class.equals(commonClass)) {
+                // if the commonClass has an equivalent primitive type, get it
+                if (isNumber) {
+                    try {
+                        Field TYPE = commonClass.getField("TYPE");
+                        commonClass = (Class<?>) TYPE.get(null);
+                    } catch (Exception xany) {
+                        // ignore
+                    }
+                }
+                // allocate and fill up the typed array
+                Object typed = Array.newInstance(commonClass, size);
+                for(int i = 0; i < size; ++i) {
+                    Array.set(typed, i, untyped[i]);
+                }
+                return typed;
+            }
+        }
+        return untyped;
+    }
+
+    /**
+     * Replace all numbers in an arguments array with the smallest type that will fit.
+     * @param args the argument array
+     * @return true if some arguments were narrowed and args array is modified,
+     *         false if no narrowing occured and args array has not been modified
+     */
+    protected boolean narrowArguments(Object[] args) {
+        boolean narrowed = false;
+        for (int a = 0; a < args.length; ++a) {
+            Object arg = args[a];
+            if (arg instanceof Number) {
+                Object narg = narrow((Number) arg);
+                if (narg != arg) {
+                    narrowed = true;
+                }
+                args[a] = narg;
+            }
+        }
+        return narrowed;
+    }
+
+    /**
+     * Add two values together.
+     * <p>
+     * If any numeric add fails on coercion to the appropriate type,
+     * treat as Strings and do concatenation.
+     * </p>
+     * @param left first value
+     * @param right second value
+     * @return left + right.
+     */
+    public Object add(Object left, Object right) {
+        if (left == null && right == null) {
+            return controlNullNullOperands();
+        }
+        
+        try {
+            // if either are floating point (double or float) use double
+            if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
+                double l = toDouble(left);
+                double r = toDouble(right);
+                return new Double(l + r);
+            }
+        
+            // if both are bigintegers use that type
+            if (left instanceof BigInteger && right instanceof BigInteger) {
+                BigInteger l = toBigInteger(left);
+                BigInteger r = toBigInteger(right);
+                return l.add(r);
+            }
+            
+            // if either are bigdecimal use that type 
+            if (left instanceof BigDecimal || right instanceof BigDecimal) {
+                BigDecimal l = toBigDecimal(left);
+                BigDecimal r = toBigDecimal(right);
+                return l.add(r);
+            }
+            
+            // otherwise treat as integers
+            BigInteger l = toBigInteger(left);
+            BigInteger r = toBigInteger(right);
+            BigInteger result = l.add(r);
+            return narrowBigInteger(left, right, result);
+        } catch (java.lang.NumberFormatException nfe) {
+            // Well, use strings!
+            return toString(left).concat(toString(right));
+        }
+    }
+
+    /**
+     * Divide the left value by the right.
+     * @param left first value
+     * @param right second value
+     * @return left / right
+     * @throws ArithmeticException if right == 0
+     */
+    public Object divide(Object left, Object right) {
+        if (left == null && right == null) {
+            return controlNullNullOperands();
+        }
+
+        // if either are floating point (double or float) use double
+        if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
+            double l = toDouble(left);
+            double r = toDouble(right);
+            if (r == 0.0) {
+                throw new ArithmeticException("/");
+            }
+            return new Double(l / r);
+        }
+
+        // if both are bigintegers use that type
+        if (left instanceof BigInteger && right instanceof BigInteger) {
+            BigInteger l = toBigInteger(left);
+            BigInteger r = toBigInteger(right);
+            return l.divide(r);
+        }
+
+        // if either are bigdecimal use that type
+        if (left instanceof BigDecimal || right instanceof BigDecimal) {
+            BigDecimal l = toBigDecimal(left);
+            BigDecimal r = toBigDecimal(right);
+            BigDecimal d = l.divide(r);
+            return d;
+        }
+
+        // otherwise treat as integers
+        BigInteger l = toBigInteger(left);
+        BigInteger r = toBigInteger(right);
+        BigInteger result = l.divide(r);
+        return narrowBigInteger(left, right, result);
+    }
+    
+    /**
+     * left value mod right.
+     * @param left first value
+     * @param right second value
+     * @return left mod right
+     * @throws ArithmeticException if right == 0.0
+     */
+    public Object mod(Object left, Object right) {
+        if (left == null && right == null) {
+            return controlNullNullOperands();
+        }
+
+        // if either are floating point (double or float) use double
+        if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
+            double l = toDouble(left);
+            double r = toDouble(right);
+            if (r == 0.0) {
+                throw new ArithmeticException("%");
+            }
+            return new Double(l % r);
+        }
+
+        // if both are bigintegers use that type
+        if (left instanceof BigInteger && right instanceof BigInteger) {
+            BigInteger l = toBigInteger(left);
+            BigInteger r = toBigInteger(right);
+            return l.mod(r);
+        }
+
+        // if either are bigdecimal use that type 
+        if (left instanceof BigDecimal || right instanceof BigDecimal) {
+            BigDecimal l = toBigDecimal(left);
+            BigDecimal r = toBigDecimal(right);
+            BigDecimal remainder = l.remainder(r);
+            return remainder;
+        }
+
+        // otherwise treat as integers
+        BigInteger l = toBigInteger(left);
+        BigInteger r = toBigInteger(right);
+        BigInteger result = l.mod(r);
+        return narrowBigInteger(left, right, result);
+    }
+    
+    /**
+     * Multiply the left value by the right.
+     * @param left first value
+     * @param right second value
+     * @return left * right.
+     */
+    public Object multiply(Object left, Object right) {
+        if (left == null && right == null) {
+            return controlNullNullOperands();
+        }
+
+        // if either are floating point (double or float) use double
+        if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
+            double l = toDouble(left);
+            double r = toDouble(right);
+            return new Double(l * r);
+        }
+        
+        // if both are bigintegers use that type
+        if (left instanceof BigInteger && right instanceof BigInteger) {
+            BigInteger l = toBigInteger(left);
+            BigInteger r = toBigInteger(right);
+            return l.multiply(r);
+        }
+        
+        // if either are bigdecimal use that type 
+        if (left instanceof BigDecimal || right instanceof BigDecimal) {
+            BigDecimal l = toBigDecimal(left);
+            BigDecimal r = toBigDecimal(right);
+            return l.multiply(r);
+        }
+
+        // otherwise treat as integers
+        BigInteger l = toBigInteger(left);
+        BigInteger r = toBigInteger(right);
+        BigInteger result = l.multiply(r);
+        return narrowBigInteger(left, right, result);
+    }
+    
+    /**
+     * Subtract the right value from the left.
+     * @param left first value
+     * @param right second value
+     * @return left - right.
+     */
+    public Object subtract(Object left, Object right) {
+        if (left == null && right == null) {
+            return controlNullNullOperands();
+        }
+
+        // if either are floating point (double or float) use double
+        if (isFloatingPointNumber(left) || isFloatingPointNumber(right)) {
+            double l = toDouble(left);
+            double r = toDouble(right);
+            return new Double(l - r);
+        }
+        
+        // if both are bigintegers use that type
+        if (left instanceof BigInteger && right instanceof BigInteger) {
+            BigInteger l = toBigInteger(left);
+            BigInteger r = toBigInteger(right);
+            return l.subtract(r);
+        }
+        
+        // if either are bigdecimal use that type 
+        if (left instanceof BigDecimal || right instanceof BigDecimal) {
+            BigDecimal l = toBigDecimal(left);
+            BigDecimal r = toBigDecimal(right);
+            return l.subtract(r);
+        }
+
+        // otherwise treat as integers
+        BigInteger l = toBigInteger(left);
+        BigInteger r = toBigInteger(right);
+        BigInteger result = l.subtract(r);
+        return narrowBigInteger(left, right, result);
+    }
+
+    /**
+     * Test if left regexp matches right.
+     *
+     * @param left first value
+     * @param right second value
+     * @return test result.
+     */
+    public boolean matches(Object left, Object right) {
+        if (left == null && right == null) {
+            //if both are null L == R
+            return true;
+        }
+        if (left == null || right == null) {
+            // we know both aren't null, therefore L != R
+            return false;
+        }
+        final String arg = left.toString();
+        if (right instanceof java.util.regex.Pattern) {
+            return ((java.util.regex.Pattern) right).matcher(arg).matches();
+        } else {
+            return arg.matches(right.toString());
+        }
+    }
+
+    /**
+     * Test if left and right are equal.
+     *
+     * @param left first value
+     * @param right second value
+     * @return test result.
+     */
+    public boolean equals(Object left, Object right) {
+        if (left == null && right == null) {
+            /*
+             * if both are null L == R
+             */
+            return true;
+        } else if (left == null || right == null) {
+            /*
+             * we know both aren't null, therefore L != R
+             */
+            return false;
+        } else if (left.getClass().equals(right.getClass())) {
+            return left.equals(right);
+        } else if (left instanceof BigDecimal || right instanceof BigDecimal) {
+            return toBigDecimal(left).compareTo(toBigDecimal(right)) == 0;
+        } else if (isFloatingPointType(left, right)) {
+            return toDouble(left) == toDouble(right);
+        } else if (left instanceof Number || right instanceof Number || left instanceof Character
+            || right instanceof Character) {
+            return toLong(left) == toLong(right);
+        } else if (left instanceof Boolean || right instanceof Boolean) {
+            return toBoolean(left) == toBoolean(right);
+        } else if (left instanceof java.lang.String || right instanceof String) {
+            return left.toString().equals(right.toString());
+        }
+
+        return left.equals(right);
+    }
+
+
+    /**
+     * Test if left < right.
+     *
+     * @param left first value
+     * @param right second value
+     * @return test result.
+     */
+    public boolean lessThan(Object left, Object right) {
+        if ((left == right) || (left == null) || (right == null)) {
+            return false;
+        } else if (isFloatingPoint(left) || isFloatingPoint(right)) {
+            double leftDouble = toDouble(left);
+            double rightDouble = toDouble(right);
+            return leftDouble < rightDouble;
+        } else if (left instanceof BigDecimal || right instanceof BigDecimal) {
+            BigDecimal l  = toBigDecimal(left);
+            BigDecimal r  = toBigDecimal(right);
+            return l.compareTo(r) < 0;
+        } else if (isNumberable(left) || isNumberable(right)) {
+            long leftLong = toLong(left);
+            long rightLong = toLong(right);
+            return leftLong < rightLong;
+        } else if (left instanceof String || right instanceof String) {
+            String leftString = left.toString();
+            String rightString = right.toString();
+            return leftString.compareTo(rightString) < 0;
+        } else if (left instanceof Comparable<?>) {
+            @SuppressWarnings("unchecked") // OK because of instanceof check above
+            final Comparable<Object> comparable = (Comparable<Object>) left;
+            return comparable.compareTo(right) < 0;
+        } else if (right instanceof Comparable<?>) {
+            @SuppressWarnings("unchecked") // OK because of instanceof check above
+            final Comparable<Object> comparable = (Comparable<Object>) right;
+            return comparable.compareTo(left) > 0;
+        }
+
+        throw new IllegalArgumentException("Invalid comparison : comparing cardinality for left: " + left
+            + " and right: " + right);
+
+    }
+
+    /**
+     * Test if left > right.
+     *
+     * @param left first value
+     * @param right second value
+     * @return test result.
+     */
+    public boolean greaterThan(Object left, Object right) {
+        if (left == null || right == null) {
+            return false;
+        }
+        return !equals(left, right) && !lessThan(left, right);
+    }
+
+    /**
+     * Test if left <= right.
+     *
+     * @param left first value
+     * @param right second value
+     * @return test result.
+     */
+    public boolean lessThanOrEqual(Object left, Object right) {
+        return equals(left, right) || lessThan(left, right);
+    }
+
+    /**
+     * Test if left >= right.
+     *
+     * @param left first value
+     * @param right second value
+     * @return test result.
+     */
+    public boolean greaterThanOrEqual(Object left, Object right) {
+        return equals(left, right) || greaterThan(left, right);
+    }
+    
+    /**
+     * Coerce to a boolean (not a java.lang.Boolean).
+     *
+     * @param val Object to be coerced.
+     * @return The boolean coerced value, or false if none possible.
+     */
+    public boolean toBoolean(Object val) {
+        if (val == null) {
+            controlNullOperand();
+            return false;
+        } else if (val instanceof Boolean) {
+            return ((Boolean) val).booleanValue();
+        } else if (val instanceof String) {
+            return Boolean.valueOf((String) val).booleanValue();
+        }
+        // TODO: is this a reasonable default?
+        return false;
+    }
+
+    /**
+     * Coerce to a int.
+     *
+     * @param val Object to be coerced.
+     * @return The int coerced value.
+     */
+    public int toInteger(Object val) {
+        if (val == null) {
+            controlNullOperand();
+            return 0;
+        } else if (val instanceof String) {
+            if ("".equals(val)) {
+                return 0;
+            }
+            return Integer.parseInt((String) val);
+        } else if (val instanceof Character) {
+            return ((Character) val).charValue();
+        } else if (val instanceof Boolean) {
+            throw new IllegalArgumentException("Boolean->Integer coercion exception");
+        } else if (val instanceof Number) {
+            return ((Number) val).intValue();
+        }
+
+        throw new IllegalArgumentException("Integer coercion exception. Can't coerce type: "
+                + val.getClass().getName());
+    }
+
+    
+    /**
+     * Coerce to a long (not a java.lang.Long).
+     *
+     * @param val Object to be coerced.
+     * @return The long coerced value.
+     */
+    public long toLong(Object val) {
+        if (val == null) {
+            controlNullOperand();
+            return 0L;
+        } else if (val instanceof String) {
+            if ("".equals(val)) {
+                return 0;
+            }
+            return Long.parseLong((String) val);
+        } else if (val instanceof Character) {
+            return ((Character) val).charValue();
+        } else if (val instanceof Boolean) {
+            throw new NumberFormatException("Boolean->Long coercion exception");
+        } else if (val instanceof Number) {
+            return ((Number) val).longValue();
+        }
+
+        throw new NumberFormatException("Long coercion exception. Can't coerce type: " + val.getClass().getName());
+    }
+
+    /**
+     * Get a BigInteger from the object passed.
+     * Null and empty string maps to zero.
+     * @param val the object to be coerced.
+     * @return a BigDecimal.
+     * @throws NullPointerException if val is null and mode is strict.
+     */
+    public BigInteger toBigInteger(Object val) {
+        if (val instanceof BigInteger) {
+            return (BigInteger) val;
+        } else if (val == null) {
+            controlNullOperand();
+            return BigInteger.valueOf(0);
+        } else if (val instanceof String) {
+            String string = (String) val;
+            if ("".equals(string.trim())) {
+                return BigInteger.ZERO;
+            }
+            return new BigInteger(string);
+        } else if (val instanceof Number) {
+            return new BigInteger(val.toString());
+        } else if (val instanceof Character) {
+            int i = ((Character) val).charValue();
+            return BigInteger.valueOf(i);
+        }
+        
+        throw new IllegalArgumentException("BigInteger coercion exception. Can't coerce type: "
+                + val.getClass().getName());
+    }
+    
+    /**
+     * Get a BigDecimal from the object passed.
+     * Null and empty string maps to zero.
+     * @param val the object to be coerced.
+     * @return a BigDecimal.
+     * @throws NullPointerException if val is null and mode is strict.
+     */
+    public BigDecimal toBigDecimal(Object val) {
+        if (val instanceof BigDecimal) {
+            return (BigDecimal) val;
+        } else if (val == null) {
+            controlNullOperand();
+            return BigDecimal.ZERO;
+        } else if (val instanceof String) {
+            String string = (String) val;
+            if ("".equals(string.trim())) {
+                return BigDecimal.valueOf(0);
+            }
+            return new BigDecimal(string);
+        } else if (val instanceof Number) {
+            return new BigDecimal(val.toString());
+        } else if (val instanceof Character) {
+            int i = ((Character) val).charValue();
+            return new BigDecimal(i);
+        }
+        
+        throw new IllegalArgumentException("BigDecimal coercion exception. Can't coerce type: "
+                + val.getClass().getName());
+    }
+    
+    /**
+     * Coerce to a double.
+     *
+     * @param val Object to be coerced.
+     * @return The double coerced value.
+     * @throws NullPointerException if val is null and mode is strict.
+     */
+    public double toDouble(Object val) {
+        if (val == null) {
+            controlNullOperand();
+            return 0;
+        } else if (val instanceof String) {
+            String string = (String) val;
+            if ("".equals(string.trim())) {
+                return 0;
+            }
+            // the spec seems to be iffy about this.  Going to give it a wack anyway
+            return Double.parseDouble(string);
+        } else if (val instanceof Character) {
+            int i = ((Character) val).charValue();
+
+            return i;
+        } else if (val instanceof Double) {
+            return ((Double) val).doubleValue();
+        } else if (val instanceof Number) {
+            //The below construct is used rather than ((Number)val).doubleValue() to ensure
+            //equality between comparing new Double( 6.4 / 3 ) and the jexl expression of 6.4 / 3
+            return Double.parseDouble(String.valueOf(val));
+        } else if (val instanceof Boolean) {
+            throw new IllegalArgumentException("Boolean->Double coercion exception");
+        }
+
+        throw new IllegalArgumentException("Double coercion exception. Can't coerce type: "
+                + val.getClass().getName());
+    }
+
+
+    /**
+     * Coerce to a string.
+     *
+     * @param val Object to be coerced.
+     * @return The String coerced value.
+     * @throws NullPointerException if val is null and mode is strict.
+     */
+    public String toString(Object val) {
+        if (val == null) {
+            controlNullOperand();
+            val = "";
+        }
+        return val.toString();
+    }
+
+    /**
+     * Given a Number, return back the value using the smallest type the result
+     * will fit into. This works hand in hand with parameter 'widening' in java
+     * method calls, e.g. a call to substring(int,int) with an int and a long
+     * will fail, but a call to substring(int,int) with an int and a short will
+     * succeed.
+     *
+     * @param original the original number.
+     * @return a value of the smallest type the original number will fit into.
+     */
+    public Number narrow(Number original) {
+        if (original == null) {
+            return original;
+        }
+        Number result = original;
+        if (original instanceof BigDecimal) {
+            BigDecimal bigd = (BigDecimal) original;
+            // if it's bigger than a double it can't be narrowed
+            if (bigd.compareTo(BIGD_DOUBLE_MAX_VALUE) > 0) {
+                return original;
+            }
+        }
+        if (original instanceof Double || original instanceof Float || original instanceof BigDecimal) {
+            double value = original.doubleValue();
+            if (value <= Float.MAX_VALUE && value >= Float.MIN_VALUE) {
+                result = Float.valueOf(result.floatValue());
+            }
+            // else it fits in a double only
+        } else {
+            if (original instanceof BigInteger) {
+                BigInteger bigi = (BigInteger) original;
+                // if it's bigger than a Long it can't be narrowed
+                if (bigi.compareTo(BIGI_LONG_MAX_VALUE) > 0
+                    || bigi.compareTo(BIGI_LONG_MIN_VALUE) < 0) {
+                    return original;
+                }
+            }
+            long value = original.longValue();
+            if (value <= Byte.MAX_VALUE && value >= Byte.MIN_VALUE) {
+                // it will fit in a byte
+                result = Byte.valueOf((byte) value);
+            } else if (value <= Short.MAX_VALUE && value >= Short.MIN_VALUE) {
+                result = Short.valueOf((short) value);
+            } else if (value <= Integer.MAX_VALUE && value >= Integer.MIN_VALUE) {
+                result = Integer.valueOf((int) value);
+            }
+            // else it fits in a long
+        }
+        return result;
+    }
+
+}
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/JexlContext.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/JexlContext.java
new file mode 100644
index 0000000..97ce59a
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/JexlContext.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+/**
+ * Manages variables which can be referenced in a JEXL expression.
+ *
+ *  @since 1.0
+ *  @version $Id$
+ */
+public interface JexlContext {
+    /**
+     * Gets the value of a variable.
+     * @param name the variable's name
+     * @return the value
+     */
+    Object get(String name);
+
+    /**
+     * Sets the value of a variable.
+     * @param name the variable's name
+     * @param value the variable's value
+     */
+    void set(String name, Object value);
+
+    /**
+     * Checks whether a variable is defined in this context.
+     * <p>A variable may be defined with a null value; this method checks whether the
+     * value is null or if the variable is undefined.</p>
+     * @param name the variable's name
+     * @return true if it exists, false otherwise
+     */
+    boolean has(String name);
+}
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/JexlEngine.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/JexlEngine.java
new file mode 100644
index 0000000..328358f
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/JexlEngine.java
@@ -0,0 +1,925 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.io.Reader;
+import java.net.URL;
+import java.net.URLConnection;
+import java.lang.ref.SoftReference;
+import java.lang.reflect.Constructor;
+import java.util.Map;
+import java.util.Set;
+import java.util.Collections;
+import java.util.Map.Entry;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.commons.jexl2.parser.ParseException;
+import org.apache.commons.jexl2.parser.Parser;
+import org.apache.commons.jexl2.parser.JexlNode;
+import org.apache.commons.jexl2.parser.TokenMgrError;
+import org.apache.commons.jexl2.parser.ASTJexlScript;
+
+import org.apache.commons.jexl2.introspection.Uberspect;
+import org.apache.commons.jexl2.introspection.UberspectImpl;
+import org.apache.commons.jexl2.introspection.JexlMethod;
+
+/**
+ * <p>
+ * Creates and evaluates Expression and Script objects.
+ * Determines the behavior of Expressions & Scripts during their evaluation with respect to:
+ * <ul>
+ *  <li>Introspection, see {@link Uberspect}</li>
+ *  <li>Arithmetic & comparison, see {@link JexlArithmetic}</li>
+ *  <li>Error reporting</li>
+ *  <li>Logging</li>
+ * </ul>
+ * </p>
+ * <p>The <code>setSilent</code>and<code>setLenient</code> methods allow to fine-tune an engine instance behavior
+ * according to various error control needs.
+ * </p>
+ * <ul>
+ * <li>When "silent" & "lenient" (not-strict):
+ * <p> 0 & null should be indicators of "default" values so that even in an case of error,
+ * something meaningfull can still be inferred; may be convenient for configurations.
+ * </p>
+ * </li>
+ * <li>When "silent" & "strict":
+ * <p>One should probably consider using null as an error case - ie, every object
+ * manipulated by JEXL should be valued; the ternary operator, especially the '?:' form
+ * can be used to workaround exceptional cases.
+ * Use case could be configuration with no implicit values or defaults.
+ * </p>
+ * </li>
+ * <li>When "not-silent" & "not-strict":
+ * <p>The error control grain is roughly on par with JEXL 1.0</p>
+ * </li>
+ * <li>When "not-silent" & "strict":
+ * <p>The finest error control grain is obtained; it is the closest to Java code -
+ * still augmented by "script" capabilities regarding automated conversions & type matching.
+ * </p>
+ * </li>
+ * </ul>
+ * <p>
+ * Note that methods that evaluate expressions may throw <em>unchecked</em> exceptions;
+ * The {@link JexlException} are thrown in "non-silent" mode but since these are
+ * RuntimeException, user-code <em>should</em> catch them wherever most appropriate.
+ * </p>
+ * @since 2.0
+ */
+public class JexlEngine {    
+    /**
+     * An empty/static/non-mutable JexlContext used instead of null context.
+     */
+    public static final JexlContext EMPTY_CONTEXT = new JexlContext() {
+        /** {@inheritDoc} */
+        public Object get(String name) {
+            return null;
+        }
+        /** {@inheritDoc} */
+        public boolean has(String name) {
+            return false;
+        }
+        /** {@inheritDoc} */
+        public void set(String name, Object value) {
+            throw new UnsupportedOperationException("Not supported in void context.");
+        }
+    };
+
+    /**
+     *  Gets the default instance of Uberspect.
+     * <p>This is lazily initialized to avoid building a default instance if there
+     * is no use for it. The main reason for not using the default Uberspect instance is to
+     * be able to use a (low level) introspector created with a given logger
+     * instead of the default one.</p>
+     * <p>Implemented as on demand holder idiom.</p>
+     */
+    private static final class UberspectHolder {
+        /** The default uberspector that handles all introspection patterns. */
+        private static final Uberspect UBERSPECT = new UberspectImpl(LogFactory.getLog(JexlEngine.class));
+        /** Non-instantiable. */
+        private UberspectHolder() {}
+    }
+    
+    /**
+     * The Uberspect instance.
+     */
+    protected final Uberspect uberspect;
+    /**
+     * The JexlArithmetic instance.
+     */
+    protected final JexlArithmetic arithmetic;
+    /**
+     * The Log to which all JexlEngine messages will be logged.
+     */
+    protected final Log logger;
+    /**
+     * The singleton ExpressionFactory also holds a single instance of
+     * {@link Parser}.
+     * When parsing expressions, ExpressionFactory synchronizes on Parser.
+     */
+    protected final Parser parser = new Parser(new StringReader(";")); //$NON-NLS-1$
+    /**
+     * Whether expressions evaluated by this engine will throw exceptions (false) or 
+     * return null (true). Default is false.
+     */
+    protected boolean silent = false;
+    /**
+     * Whether error messages will carry debugging information.
+     */
+    protected boolean debug = true;
+    /**
+     *  The map of 'prefix:function' to object implementing the function.
+     */
+    protected Map<String, Object> functions = Collections.emptyMap();
+    /**
+     * The expression cache.
+     */
+    protected SoftCache<String, ASTJexlScript> cache = null;
+    /**
+     * The default cache load factor.
+     */
+    private static final float LOAD_FACTOR = 0.75f;
+
+    /**
+     * Creates an engine with default arguments.
+     */
+    public JexlEngine() {
+        this(null, null, null, null);
+    }
+
+    /**
+     * Creates a JEXL engine using the provided {@link Uberspect}, (@link JexlArithmetic),
+     * a function map and logger.
+     * @param anUberspect to allow different introspection behaviour
+     * @param anArithmetic to allow different arithmetic behaviour
+     * @param theFunctions an optional map of functions (@link setFunctions)
+     * @param log the logger for various messages
+     */
+    public JexlEngine(Uberspect anUberspect, JexlArithmetic anArithmetic, Map<String, Object> theFunctions, Log log) {
+        this.uberspect = anUberspect == null ? getUberspect(log) : anUberspect;
+        if (log == null) {
+            log = LogFactory.getLog(JexlEngine.class);
+        }
+        this.logger = log;
+        this.arithmetic = anArithmetic == null ? new JexlArithmetic(true) : anArithmetic;
+        if (theFunctions != null) {
+            this.functions = theFunctions;
+        }
+    }
+
+
+    /**
+     *  Gets the default instance of Uberspect.
+     * <p>This is lazily initialized to avoid building a default instance if there
+     * is no use for it. The main reason for not using the default Uberspect instance is to
+     * be able to use a (low level) introspector created with a given logger
+     * instead of the default one.</p>
+     * @param logger the logger to use for the underlying Uberspect
+     * @return Uberspect the default uberspector instance.
+     */
+    public static Uberspect getUberspect(Log logger) {
+        if (logger == null || logger.equals(LogFactory.getLog(JexlEngine.class))) {
+            return UberspectHolder.UBERSPECT;
+        }
+        return new UberspectImpl(logger);
+    }
+
+    /**
+     * Gets this engine underlying uberspect.
+     * @return the uberspect
+     */
+    public Uberspect getUberspect() {
+        return uberspect;
+    }
+
+    /**
+     * Sets whether this engine reports debugging information when error occurs.
+     * <p>This method is <em>not</em> thread safe; it should be called as an optional step of the JexlEngine
+     * initialization code before expression creation &amp; evaluation.</p>
+     * @see JexlEngine#setSilent
+     * @see JexlEngine#setLenient
+     * @param flag true implies debug is on, false implies debug is off.
+     */
+    public void setDebug(boolean flag) {
+        this.debug = flag;
+    }
+
+    /**
+     * Checks whether this engine is in debug mode.
+     * @return true if debug is on, false otherwise
+     */
+    public boolean isDebug() {
+        return this.debug;
+    }
+
+    /**
+     * Sets whether this engine throws JexlException during evaluation when an error is triggered.
+     * <p>This method is <em>not</em> thread safe; it should be called as an optional step of the JexlEngine
+     * initialization code before expression creation &amp; evaluation.</p>
+     * @see JexlEngine#setDebug
+     * @see JexlEngine#setLenient
+     * @param flag true means no JexlException will occur, false allows them
+     */
+    public void setSilent(boolean flag) {
+        this.silent = flag;
+    }
+
+    /**
+     * Checks whether this engine throws JexlException during evaluation.
+     * @return true if silent, false (default) otherwise
+     */
+    public boolean isSilent() {
+        return this.silent;
+    }
+
+    /**
+     * Sets whether this engine triggers errors during evaluation when null is used as
+     * an operand.
+     * <p>This method is <em>not</em> thread safe; it should be called as an optional step of the JexlEngine
+     * initialization code before expression creation &amp; evaluation.</p>
+     * @see JexlEngine#setSilent
+     * @see JexlEngine#setDebug
+     * @param flag true means no JexlException will occur, false allows them
+     */
+    public void setLenient(boolean flag) {
+        this.arithmetic.setLenient(flag);
+    }
+
+    /**
+     * Checks whether this engine triggers errors during evaluation when null is used as
+     * an operand.
+     * @return true if lenient, false if strict
+     */
+    public boolean isLenient() {
+        return this.arithmetic.isLenient();
+    }
+
+    /**
+     * Sets the class loader used to discover classes in 'new' expressions.
+     * <p>This method should be called as an optional step of the JexlEngine
+     * initialization code before expression creation &amp; evaluation.</p>
+     * @param loader the class loader to use
+     */
+    public void setClassLoader(ClassLoader loader) {
+        uberspect.setClassLoader(loader);
+    }
+
+    /**
+     * Sets a cache of the defined size for expressions.
+     * @param size if not strictly positive, no cache is used.
+     */
+    public void setCache(int size) {
+        // since the cache is only used during parse, use same sync object
+        synchronized (parser) {
+            if (size <= 0) {
+                cache = null;
+            } else if (cache == null || cache.size() != size) {
+                cache = new SoftCache<String, ASTJexlScript>(size);
+            }
+        }
+    }
+
+    /**
+     * Sets the map of function namespaces.
+     * <p>
+     * This method is <em>not</em> thread safe; it should be called as an optional step of the JexlEngine
+     * initialization code before expression creation &amp; evaluation.
+     * </p>
+     * <p>
+     * Each entry key is used as a prefix, each entry value used as a bean implementing
+     * methods; an expression like 'nsx:method(123)' will thus be solved by looking at
+     * a registered bean named 'nsx' that implements method 'method' in that map.
+     * If all methods are static, you may use the bean class instead of an instance as value.
+     * </p>
+     * <p>
+     * If the entry value is a class that has one contructor taking a JexlContext as argument, an instance
+     * of the namespace will be created at evaluation time. It might be a good idea to derive a JexlContext
+     * to carry the information used by the namespace to avoid variable space pollution and strongly type
+     * the constructor with this specialized JexlContext.
+     * </p>
+     * <p>
+     * The key or prefix allows to retrieve the bean that plays the role of the namespace.
+     * If the prefix is null, the namespace is the top-level namespace allowing to define
+     * top-level user defined functions ( ie: myfunc(...) )
+     * </p>
+     * @param funcs the map of functions that should not mutate after the call; if null
+     * is passed, the empty collection is used.
+     */
+    public void setFunctions(Map<String, Object> funcs) {
+        functions = funcs != null ? funcs : Collections.<String, Object>emptyMap();
+    }
+
+    /**
+     * Retrieves the map of function namespaces.
+     *
+     * @return the map passed in setFunctions or the empty map if the
+     * original was null.
+     */
+    public Map<String, Object> getFunctions() {
+        return functions;
+    }
+
+    /**
+     * An overridable through covariant return Expression creator.
+     * @param text the script text
+     * @param tree the parse AST tree
+     * @return the script instance
+     */
+    protected Expression createExpression(ASTJexlScript tree, String text) {
+        return new ExpressionImpl(this, text, tree);
+    }
+    
+    /**
+     * Creates an Expression from a String containing valid
+     * JEXL syntax.  This method parses the expression which
+     * must contain either a reference or an expression.
+     * @param expression A String containing valid JEXL syntax
+     * @return An Expression object which can be evaluated with a JexlContext
+     * @throws JexlException An exception can be thrown if there is a problem
+     *      parsing this expression, or if the expression is neither an
+     *      expression nor a reference.
+     */
+    public Expression createExpression(String expression) {
+        return createExpression(expression, null);
+    }
+
+    /**
+     * Creates an Expression from a String containing valid
+     * JEXL syntax.  This method parses the expression which
+     * must contain either a reference or an expression.
+     * @param expression A String containing valid JEXL syntax
+     * @return An Expression object which can be evaluated with a JexlContext
+     * @param info An info structure to carry debugging information if needed
+     * @throws JexlException An exception can be thrown if there is a problem
+     *      parsing this expression, or if the expression is neither an
+     *      expression or a reference.
+     */
+    public Expression createExpression(String expression, JexlInfo info) {
+        // Parse the expression
+        ASTJexlScript tree = parse(expression, info);
+        if (tree.jjtGetNumChildren() > 1) {
+            logger.warn("The JEXL Expression created will be a reference"
+                      + " to the first expression from the supplied script: \"" + expression + "\" ");
+        }
+        return createExpression(tree, expression);
+    }
+
+    /**
+     * Creates a Script from a String containing valid JEXL syntax.
+     * This method parses the script which validates the syntax.
+     *
+     * @param scriptText A String containing valid JEXL syntax
+     * @return A {@link Script} which can be executed using a {@link JexlContext}.
+     * @throws JexlException if there is a problem parsing the script.
+     */
+    public Script createScript(String scriptText) {
+        return createScript(scriptText, null);
+    }
+
+    /**
+     * Creates a Script from a String containing valid JEXL syntax.
+     * This method parses the script which validates the syntax.
+     *
+     * @param scriptText A String containing valid JEXL syntax
+     * @param info An info structure to carry debugging information if needed
+     * @return A {@link Script} which can be executed using a {@link JexlContext}.
+     * @throws JexlException if there is a problem parsing the script.
+     */
+    public Script createScript(String scriptText, JexlInfo info) {
+        if (scriptText == null) {
+            throw new NullPointerException("scriptText is null");
+        }
+        // Parse the expression
+        ASTJexlScript tree = parse(scriptText, info);
+        return createScript(tree, scriptText);
+    }
+
+    /**
+     * An overridable through covariant return Script creator.
+     * @param text the script text
+     * @param tree the parse AST tree
+     * @return the script instance
+     */
+    protected Script createScript(ASTJexlScript tree, String text) {
+        return new ExpressionImpl(this, text, tree);
+    }
+    
+    /**
+     * Creates a Script from a {@link File} containing valid JEXL syntax.
+     * This method parses the script and validates the syntax.
+     *
+     * @param scriptFile A {@link File} containing valid JEXL syntax.
+     *      Must not be null. Must be a readable file.
+     * @return A {@link Script} which can be executed with a
+     *      {@link JexlContext}.
+     * @throws IOException if there is a problem reading the script.
+     * @throws JexlException if there is a problem parsing the script.
+     */
+    public Script createScript(File scriptFile) throws IOException {
+        if (scriptFile == null) {
+            throw new NullPointerException("scriptFile is null");
+        }
+        if (!scriptFile.canRead()) {
+            throw new IOException("Can't read scriptFile (" + scriptFile.getCanonicalPath() + ")");
+        }
+        BufferedReader reader = new BufferedReader(new FileReader(scriptFile));
+        JexlInfo info = null;
+        if (debug) {
+            info = createInfo(scriptFile.getName(), 0, 0);
+        }
+        return createScript(readerToString(reader), info);
+    }
+
+    /**
+     * Creates a Script from a {@link URL} containing valid JEXL syntax.
+     * This method parses the script and validates the syntax.
+     *
+     * @param scriptUrl A {@link URL} containing valid JEXL syntax.
+     *      Must not be null. Must be a readable file.
+     * @return A {@link Script} which can be executed with a
+     *      {@link JexlContext}.
+     * @throws IOException if there is a problem reading the script.
+     * @throws JexlException if there is a problem parsing the script.
+     */
+    public Script createScript(URL scriptUrl) throws IOException {
+        if (scriptUrl == null) {
+            throw new NullPointerException("scriptUrl is null");
+        }
+        URLConnection connection = scriptUrl.openConnection();
+
+        BufferedReader reader = new BufferedReader(
+                new InputStreamReader(connection.getInputStream()));
+        JexlInfo info = null;
+        if (debug) {
+            info = createInfo(scriptUrl.toString(), 0, 0);
+        }
+        return createScript(readerToString(reader), info);
+    }
+
+    /**
+     * Accesses properties of a bean using an expression.
+     * <p>
+     * jexl.get(myobject, "foo.bar"); should equate to
+     * myobject.getFoo().getBar(); (or myobject.getFoo().get("bar"))
+     * </p>
+     * <p>
+     * If the JEXL engine is silent, errors will be logged through its logger as warning.
+     * </p>
+     * @param bean the bean to get properties from
+     * @param expr the property expression
+     * @return the value of the property
+     * @throws JexlException if there is an error parsing the expression or during evaluation
+     */
+    public Object getProperty(Object bean, String expr) {
+        return getProperty(null, bean, expr);
+    }
+
+    /**
+     * Accesses properties of a bean using an expression.
+     * <p>
+     * If the JEXL engine is silent, errors will be logged through its logger as warning.
+     * </p>
+     * @param context the evaluation context
+     * @param bean the bean to get properties from
+     * @param expr the property expression
+     * @return the value of the property
+     * @throws JexlException if there is an error parsing the expression or during evaluation
+     */
+    public Object getProperty(JexlContext context, Object bean, String expr) {
+        if (context == null) {
+            context = EMPTY_CONTEXT;
+        }
+        // lets build 1 unique & unused identifiers wrt context
+        String r0 = "$0";
+        for (int s = 0; context.has(r0); ++s) {
+            r0 = r0 + s;
+        }
+        expr = r0 + (expr.charAt(0) == '[' ? "" : ".") + expr + ";";
+        try {
+            JexlNode tree = parse(expr, null);
+            JexlNode node = tree.jjtGetChild(0);
+            Interpreter interpreter = createInterpreter(context);
+            // ensure 4 objects in register array
+            Object[] r = {r0, bean, r0, bean};
+            interpreter.setRegisters(r);
+            return node.jjtAccept(interpreter, null);
+        } catch (JexlException xjexl) {
+            if (silent) {
+                logger.warn(xjexl.getMessage(), xjexl.getCause());
+                return null;
+            }
+            throw xjexl;
+        }
+    }
+
+    /**
+     * Assign properties of a bean using an expression.
+     * <p>
+     * jexl.set(myobject, "foo.bar", 10); should equate to
+     * myobject.getFoo().setBar(10); (or myobject.getFoo().put("bar", 10) )
+     * </p>
+     * <p>
+     * If the JEXL engine is silent, errors will be logged through its logger as warning.
+     * </p>
+     * @param bean the bean to set properties in
+     * @param expr the property expression
+     * @param value the value of the property
+     * @throws JexlException if there is an error parsing the expression or during evaluation
+     */
+    public void setProperty(Object bean, String expr, Object value) {
+        setProperty(null, bean, expr, value);
+    }
+
+    /**
+     * Assign properties of a bean using an expression.
+     * <p>
+     * If the JEXL engine is silent, errors will be logged through its logger as warning.
+     * </p>
+     * @param context the evaluation context
+     * @param bean the bean to set properties in
+     * @param expr the property expression
+     * @param value the value of the property
+     * @throws JexlException if there is an error parsing the expression or during evaluation
+     */
+    public void setProperty(JexlContext context, Object bean, String expr, Object value) {
+        if (context == null) {
+            context = EMPTY_CONTEXT;
+        }
+        // lets build 2 unique & unused identifiers wrt context
+        String r0 = "$0", r1 = "$1";
+        for (int s = 0; context.has(r0); ++s) {
+            r0 = r0 + s;
+        }
+        for (int s = 0; context.has(r1); ++s) {
+            r1 = r1 + s;
+        }
+        // synthetize expr
+        expr = r0 + (expr.charAt(0) == '[' ? "" : ".") + expr + "=" + r1 + ";";
+        try {
+            JexlNode tree = parse(expr, null);
+            JexlNode node = tree.jjtGetChild(0);
+            Interpreter interpreter = createInterpreter(context);
+            // set the registers
+            Object[] r = {r0, bean, r1, value};
+            interpreter.setRegisters(r);
+            node.jjtAccept(interpreter, null);
+        } catch (JexlException xjexl) {
+            if (silent) {
+                logger.warn(xjexl.getMessage(), xjexl.getCause());
+                return;
+            }
+            throw xjexl;
+        }
+    }
+
+    /**
+     * Invokes an object's method by name and arguments.
+     * @param obj the method's invoker object
+     * @param meth the method's name
+     * @param args the method's arguments
+     * @return the method returned value or null if it failed and engine is silent
+     * @throws JexlException if method could not be found or failed and engine is not silent
+     */
+    public Object invokeMethod(Object obj, String meth, Object... args) {
+        JexlException xjexl = null;
+        Object result = null;
+        JexlInfo info = debugInfo();
+        try {
+            JexlMethod method = uberspect.getMethod(obj, meth, args, info);
+            if (method == null && arithmetic.narrowArguments(args)) {
+                method = uberspect.getMethod(obj, meth, args, info);
+            }
+            if (method != null) {
+                result = method.invoke(obj, args);
+            } else {
+                xjexl = new JexlException(info, "failed finding method " + meth);
+            }
+        } catch (Exception xany) {
+            xjexl = new JexlException(info, "failed executing method " + meth, xany);
+        } finally {
+            if (xjexl != null) {
+                if (silent) {
+                    logger.warn(xjexl.getMessage(), xjexl.getCause());
+                    return null;
+                }
+                throw xjexl;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Creates a new instance of an object using the most appropriate constructor
+     * based on the arguments.
+     * @param <T> the type of object
+     * @param clazz the class to instantiate
+     * @param args the constructor arguments
+     * @return the created object instance or null on failure when silent
+     */
+    public <T> T newInstance(Class<? extends T> clazz, Object...args) {
+        return clazz.cast(doCreateInstance(clazz, args));
+    }
+
+    /**
+     * Creates a new instance of an object using the most appropriate constructor
+     * based on the arguments.
+     * @param clazz the name of the class to instantiate resolved through this engine's class loader
+     * @param args the constructor arguments
+     * @return the created object instance or null on failure when silent
+     */
+    public Object newInstance(String clazz, Object...args) {
+       return doCreateInstance(clazz, args);
+    }
+
+    /**
+     * Creates a new instance of an object using the most appropriate constructor
+     * based on the arguments.
+     * @param clazz the class to instantiate
+     * @param args the constructor arguments
+     * @return the created object instance or null on failure when silent
+     */
+    protected Object doCreateInstance(Object clazz, Object...args) {
+        JexlException xjexl = null;
+        Object result = null;
+        JexlInfo info = debugInfo();
+        try {
+            Constructor<?> ctor = uberspect.getConstructor(clazz, args, info);
+            if (ctor == null && arithmetic.narrowArguments(args)) {
+                ctor = uberspect.getConstructor(clazz, args, info);
+            }
+            if (ctor != null) {
+                result = ctor.newInstance(args);
+            } else {
+                xjexl = new JexlException(info, "failed finding constructor for " + clazz.toString());
+            }
+        } catch (Exception xany) {
+            xjexl = new JexlException(info, "failed executing constructor for " + clazz.toString(), xany);
+        } finally {
+            if (xjexl != null) {
+                if (silent) {
+                    logger.warn(xjexl.getMessage(), xjexl.getCause());
+                    return null;
+                }
+                throw xjexl;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Creates an interpreter.
+     * @param context a JexlContext; if null, the EMPTY_CONTEXT is used instead.
+     * @return an Interpreter
+     */
+    protected Interpreter createInterpreter(JexlContext context) {
+        if (context == null) {
+            context = EMPTY_CONTEXT;
+        }
+        return new Interpreter(this, context);
+    }
+
+    /**
+     * A soft reference on cache.
+     * <p>The cache is held through a soft reference, allowing it to be GCed under
+     * memory pressure.</p>
+     * @param <K> the cache key entry type
+     * @param <V> the cache key value type
+     */
+    protected class SoftCache<K, V> {
+        /**
+         * The cache size.
+         */
+        private final int size;
+        /**
+         * The soft reference to the cache map.
+         */
+        private SoftReference<Map<K, V>> ref = null;
+
+        /**
+         * Creates a new instance of a soft cache.
+         * @param theSize the cache size
+         */
+        SoftCache(int theSize) {
+            size = theSize;
+        }
+
+        /**
+         * Returns the cache size.
+         * @return the cache size
+         */
+        int size() {
+            return size;
+        }
+
+        /**
+         * Produces the cache entry set.
+         * @return the cache entry set
+         */
+        Set<Entry<K, V>> entrySet() {
+            Map<K, V> map = ref != null ? ref.get() : null;
+            return map != null ? map.entrySet() : Collections.<Entry<K, V>>emptySet();
+        }
+
+        /**
+         * Gets a value from cache.
+         * @param key the cache entry key
+         * @return the cache entry value
+         */
+        V get(K key) {
+            final Map<K, V> map = ref != null ? ref.get() : null;
+            return map != null ? map.get(key) : null;
+        }
+
+        /**
+         * Puts a value in cache.
+         * @param key the cache entry key
+         * @param script the cache entry value
+         */
+        void put(K key, V script) {
+            Map<K, V> map = ref != null ? ref.get() : null;
+            if (map == null) {
+                map = createCache(size);
+                ref = new SoftReference<Map<K, V>>(map);
+            }
+            map.put(key, script);
+        }
+    }
+
+    /**
+     * Creates a cache.
+     * @param <K> the key type
+     * @param <V> the value type
+     * @param cacheSize the cache size, must be > 0
+     * @return a Map usable as a cache bounded to the given size
+     */
+    protected <K, V> Map<K, V> createCache(final int cacheSize) {
+        return new java.util.LinkedHashMap<K, V>(cacheSize, LOAD_FACTOR, true) {
+            /** Serial version UID. */
+            private static final long serialVersionUID = 3801124242820219131L;
+
+            @Override
+            protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
+                return size() > cacheSize;
+            }
+        };
+    }
+
+    /**
+     * Parses an expression.
+     * @param expression the expression to parse
+     * @param info debug information structure
+     * @return the parsed tree
+     * @throws JexlException if any error occured during parsing
+     */
+    protected ASTJexlScript parse(CharSequence expression, JexlInfo info) {
+        String expr = cleanExpression(expression);
+        ASTJexlScript tree = null;
+        synchronized (parser) {
+            if (cache != null) {
+                tree = cache.get(expr);
+                if (tree != null) {
+                    return tree;
+                }
+            }
+            try {
+                Reader reader = new StringReader(expr);
+                // use first calling method of JexlEngine as debug info
+                if (info == null) {
+                    info = debugInfo();
+                }
+                tree = parser.parse(reader, info);
+                if (cache != null) {
+                    cache.put(expr, tree);
+                }
+            } catch (TokenMgrError xtme) {
+                throw new JexlException(info, "tokenization failed", xtme);
+            } catch (ParseException xparse) {
+                throw new JexlException(info, "parsing failed", xparse);
+            }
+        }
+        return tree;
+    }
+
+    /**
+     * Creates a JexlInfo instance.
+     * @param fn url/file name
+     * @param l line number
+     * @param c column number
+     * @return a JexlInfo instance
+     */
+    protected JexlInfo createInfo(String fn, int l, int c) {
+        return new DebugInfo(fn, l, c);
+    }
+
+    /**
+     * Creates and fills up debugging information.
+     * <p>This gathers the class, method and line number of the first calling method
+     * not owned by JexlEngine, UnifiedJEXL or {Script,Expression}Factory.</p>
+     * @return an Info if debug is set, null otherwise
+     */
+    protected JexlInfo debugInfo() {
+        JexlInfo info = null;
+        if (debug) {
+            Throwable xinfo = new Throwable();
+            xinfo.fillInStackTrace();
+            StackTraceElement[] stack = xinfo.getStackTrace();
+            StackTraceElement se = null;
+            Class<?> clazz = getClass();
+            for (int s = 1; s < stack.length; ++s, se = null) {
+                se = stack[s];
+                String className = se.getClassName();
+                if (!className.equals(clazz.getName())) {
+                    // go deeper if called from JexlEngine or UnifiedJEXL
+                    if (className.equals(JexlEngine.class.getName())) {
+                        clazz = JexlEngine.class;
+                    } else if (className.equals(UnifiedJEXL.class.getName())) {
+                        clazz = UnifiedJEXL.class;
+                    } else {
+                        break;
+                    }
+                }
+            }
+            if (se != null) {
+                info = createInfo(se.getClassName() + "." + se.getMethodName(), se.getLineNumber(), 0);
+            }
+        }
+        return info;
+    }
+
+    /**
+     * Trims the expression from front & ending spaces.
+     * @param str expression to clean
+     * @return trimmed expression ending in a semi-colon
+     */
+    public static final String cleanExpression(CharSequence str) {
+        if (str != null) {
+            int start = 0;
+            int end = str.length();
+            if (end > 0) {
+                // trim front spaces
+                while (start < end && str.charAt(start) == ' ') {
+                    ++start;
+                }
+                // trim ending spaces
+                while (end > 0 && str.charAt(end - 1) == ' ') {
+                    --end;
+                }
+                return str.subSequence(start, end).toString();
+            }
+            return "";
+        }
+        return null;
+    }
+
+    /**
+     * Read from a reader into a StringBuffer and return a String with
+     * the contents of the reader.
+     * @param scriptReader to be read.
+     * @return the contents of the reader as a String.
+     * @throws IOException on any error reading the reader.
+     */
+    public static final String readerToString(Reader scriptReader) throws IOException {
+        StringBuilder buffer = new StringBuilder();
+        BufferedReader reader;
+        if (scriptReader instanceof BufferedReader) {
+            reader = (BufferedReader) scriptReader;
+        } else {
+            reader = new BufferedReader(scriptReader);
+        }
+        try {
+            String line;
+            while ((line = reader.readLine()) != null) {
+                buffer.append(line).append('\n');
+            }
+            return buffer.toString();
+        } finally {
+            try {
+                reader.close();
+            } catch(IOException xio) {
+                // ignore
+            }
+        }
+
+    }
+}
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/JexlException.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/JexlException.java
new file mode 100644
index 0000000..be08fcf
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/JexlException.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+import org.apache.commons.jexl2.parser.JexlNode;
+
+/**
+ * Wraps any error that might occur during interpretation of a script or expression.
+ * @since 2.0
+ */
+public class JexlException extends RuntimeException {
+    /** Serial version UID. */
+    private static final long serialVersionUID = 2690666400232612395L;
+    /** The point of origin for this exception. */
+    protected final JexlNode mark;
+    /** The debug info. */
+    protected final JexlInfo info;
+    /** A marker to use in NPEs stating a null operand error. */
+    public static final String NULL_OPERAND = "jexl.null";
+    /**
+     * Creates a new JexlException.
+     * @param node the node causing the error
+     * @param msg the error message
+     */
+    public JexlException(JexlNode node, String msg) {
+        super(msg);
+        mark = node;
+        info = node != null? node.getInfo() : null;
+
+    }
+
+    /**
+     * Creates a new JexlException.
+     * @param node the node causing the error
+     * @param msg the error message
+     * @param cause the exception causing the error
+     */
+    public JexlException(JexlNode node, String msg, Throwable cause) {
+        super(msg, cause);
+        mark = node;
+        info = node != null? node.getInfo() : null;
+    }
+    
+    /**
+     * Creates a new JexlException.
+     * @param dbg the debugging information associated
+     * @param msg the error message
+     */
+    public JexlException(JexlInfo dbg, String msg) {
+        super(msg);
+        mark = null;
+        info = dbg;
+    }
+
+    /**
+     * Creates a new JexlException.
+     * @param dbg the debugging information associated
+     * @param msg the error message
+     * @param cause the exception causing the error
+     */
+    public JexlException(JexlInfo dbg, String msg, Throwable cause) {
+        super(msg, cause);
+        mark = null;
+        info = dbg;
+    }
+    
+    /**
+     * Gets information about the cause of this error.
+     * <p>
+     * The returned string represents the outermost expression in error.
+     * The info parameter, an int[2] optionally provided by the caller, will be filled with the begin/end offset
+     * characters of the precise error's trigger.
+     * </p>
+     * @param offsets character offset interval of the precise node triggering the error
+     * @return a string representation of the offending expression, the empty string if it could not be determined
+     */
+    public String getInfo(int[] offsets) {
+        Debugger dbg = new Debugger();
+        if (dbg.debug(mark)) {
+            if (offsets != null && offsets.length >= 2) {
+                offsets[0] = dbg.start();
+                offsets[1] = dbg.end();
+            }
+            return dbg.data();
+        }
+        return "";
+    }
+    
+    /**
+     * Detailed info message about this error.
+     * Format is "debug![begin,end]: string \n msg" where:
+     * - debug is the debugging information if it exists (@link JexlEngine.setDebug)
+     * - begin, end are character offsets in the string for the precise location of the error
+     * - string is the string representation of the offending expression
+     * - msg is the actual explanation message for this error
+     * @return this error as a string
+     */
+    @Override
+    public String getMessage() {
+        Debugger dbg = new Debugger();
+        StringBuilder msg = new StringBuilder();
+        if (info != null) {
+            msg.append(info.debugString());
+        }
+        if (dbg.debug(mark)) {
+            msg.append("![");
+            msg.append(dbg.start());
+            msg.append(",");
+            msg.append(dbg.end());
+            msg.append("]: '");
+            msg.append(dbg.data());
+            msg.append("' ");
+        }
+        msg.append(super.getMessage());
+        Throwable cause = getCause();
+        if (cause != null && NULL_OPERAND == cause.getMessage()) {
+            msg.append(" caused by null operand");
+        }
+        return msg.toString();
+    }
+}
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/JexlInfo.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/JexlInfo.java
new file mode 100644
index 0000000..86d309a
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/JexlInfo.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+/**
+ * Interface for objects carrying information usefull to debugging.
+ * @since 1.0
+ */
+public interface JexlInfo {
+    /**
+     * Formats this information for debugging purpose.
+     * @return a human readable string.
+     */
+    String debugString();
+}
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/Main.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/Main.java
new file mode 100644
index 0000000..a040319
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/Main.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.InputStreamReader;
+
+/**
+ * Test application for JEXL.
+ *
+ * @since 2.0
+ */
+public class Main {
+    /**
+     * Test application for JEXL
+     * 
+     * If a single argument is present, it is treated as a filename of a JEXL
+     * script to be executed as a script. Any exceptions terminate the application.
+     * 
+     * Otherwise, lines are read from standard input and evaluated.
+     * ParseExceptions and JexlExceptions are logged, and do not cause the application to exit.
+     * This is done so that interactive testing is easier.
+     * 
+     * @param args (optional) filename to execute. Stored in the args variable.
+     * 
+     * @throws Exception if parsing or IO fail
+     */
+    public static void main(String[] args) throws Exception {
+        JexlEngine engine = new JexlEngine();
+        JexlContext context = new MapContext();
+        context.set("args", args);
+        if (args.length == 1) {
+            Script script = engine.createScript(new File(args[0]));
+            Object value = script.execute(context);
+            System.out.println("Return value: " + value);
+        } else {
+            BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
+            String line;
+            System.out.print("> ");
+            while (null != (line = console.readLine())) {
+                try {
+                    Expression expression = engine.createExpression(line);
+                    Object value = expression.evaluate(context);
+                    System.out.println("Return value: " + value);
+                } catch (JexlException e) {
+                    System.out.println(e.getLocalizedMessage());
+                }
+                System.out.print("> ");
+            }
+        }
+    }
+}
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/MapContext.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/MapContext.java
new file mode 100644
index 0000000..123cfc4
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/MapContext.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Wraps a map in a context.
+ * <p>Each entry in the map is considered a variable name, value pair.</p>
+ */
+public class MapContext implements JexlContext {
+    /**
+     * The wrapped variable map.
+     */
+    protected final Map<String, Object> map;
+
+    /**
+     * Creates a MapContext on an automatically allocated underlying HashMap.
+     */
+    public MapContext() {
+        this(null);
+    }
+
+    /**
+     * Creates a MapContext wrapping an existing user provided map.
+     * @param vars the variable map
+     */
+    public MapContext(Map<String, Object> vars) {
+        super();
+        map = vars == null ? new HashMap<String, Object>() : vars;
+    }
+
+    /** {@inheritDoc} */
+    public boolean has(String name) {
+        return map.containsKey(name);
+    }
+
+    /** {@inheritDoc} */
+    public Object get(String name) {
+        return map.get(name);
+    }
+
+    /** {@inheritDoc} */
+    public void set(String name, Object value) {
+        map.put(name, value);
+    }
+}
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/Script.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/Script.java
new file mode 100644
index 0000000..0b9892b
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/Script.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+/**
+ * <p>A JEXL Script.</p>
+ * <p>A script is some valid JEXL syntax to be executed with
+ * a given set of {@link JexlContext} variabless.</p>
+ * <p>A script is a group of statements, separated by semicolons.</p>
+ * <p>The statements can be <code>blocks</code> (curly braces containing code),
+ * Control statements such as <code>if</code> and <code>while</code>
+ * as well as expressions and assignment statements.</p>
+ *  
+ * @since 1.1
+ */
+public interface Script {
+    /**
+     * Executes the script with the variables contained in the
+     * supplied {@link JexlContext}. 
+     * 
+     * @param context A JexlContext containing variables.
+     * @return The result of this script, usually the result of 
+     *      the last statement.
+     */
+    Object execute(JexlContext context);
+
+    /**
+     * Returns the text of this Script.
+     * @return The script to be executed.
+     */
+    String getText();
+
+}
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/UnifiedJEXL.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/UnifiedJEXL.java
new file mode 100644
index 0000000..76942cf
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/UnifiedJEXL.java
@@ -0,0 +1,986 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+import java.util.ArrayList;
+import org.apache.commons.jexl2.parser.JexlNode;
+import org.apache.commons.jexl2.parser.StringParser;
+
+/**
+ * An evaluator similar to the Unified EL evaluator used in JSP/JSF based on JEXL.
+ * It is intended to be used in configuration modules, XML based frameworks or JSP taglibs
+ * and facilitate the implementation of expression evaluation.
+ * <p>
+ * An expression can mix immediate, deferred and nested sub-expressions as well as string constants;
+ * <ul>
+ * <li>The "immediate" syntax is of the form <code>"...${jexl-expr}..."</code></li>
+ * <li>The "deferred" syntax is of the form <code>"...#{jexl-expr}..."</code></li>
+ * <li>The "nested" syntax is of the form <code>"...#{...${jexl-expr0}...}..."</code></li>
+ * <li>The "composite" syntax is of the form <code>"...${jexl-expr0}... #{jexl-expr1}..."</code></li>
+ * </ul>
+ * </p>
+ * <p>
+ * Deferred & immediate expression carry different intentions:
+ * <ul>
+ * <li>An immediate expression indicate that evaluation is intended to be performed close to
+ * the definition/parsing point.</li>
+ * <li>A deferred expression indicate that evaluation is intended to occur at a later stage.</li>
+ * </ul>
+ * </p>
+ * <p>
+ * For instance: <code>"Hello ${name}, now is #{time}"</code> is a composite "deferred" expression since one
+ * of its subexpressions is deferred. Furthermore, this (composite) expression intent is
+ * to perform two evaluations; one close to its definition and another one in a later
+ * phase.
+ * </p>
+ * <p>
+ * The API reflects this feature in 2 methods, prepare and evaluate. The prepare method
+ * will evaluate the immediate subexpression and return an expression that contains only
+ * the deferred subexpressions (& constants), a prepared expression. Such a prepared expression
+ * is suitable for a later phase evaluation that may occur with a different JexlContext.
+ * Note that it is valid to call evaluate without prepare in which case the same JexlContext
+ * is used for the 2 evaluation phases.
+ * </p>
+ * <p>
+ * In the most common use-case where deferred expressions are to be kept around as properties of objects,
+ * one should parse & prepare an expression before storing it and evaluate it each time
+ * the property storing it is accessed.
+ * </p>
+ * <p>
+ * Note that nested expression use the JEXL syntax as in:
+ * <code>"#{${bar}+'.charAt(2)'}"</code>
+ * The most common mistake leading to an invalid expression being the following:
+ * <code>"#{${bar}charAt(2)}"</code>
+ * </p>
+ * <p>Also note that methods that parse evaluate expressions may throw <em>unchecked</em> ecxeptions;
+ * The {@link UnifiedJEXL.Exception} are thrown when the engine instance is in "non-silent" mode
+ * but since these are RuntimeException, user-code <em>should</em> catch them where appropriate.
+ * </p>
+ * @since 2.0
+ */
+public final class UnifiedJEXL {
+    /** The JEXL engine instance. */
+    private final JexlEngine jexl;
+    /** The expression cache. */
+    private final JexlEngine.SoftCache<String,Expression> cache;
+    /** The default cache size. */
+    private static final int CACHE_SIZE = 256;
+    /**
+     * Creates a new instance of UnifiedJEXL with a default size cache.
+     * @param aJexl the JexlEngine to use.
+     */
+    public UnifiedJEXL(JexlEngine aJexl) {
+        this(aJexl, CACHE_SIZE);
+    }
+
+    /**
+     * Creates a new instance of UnifiedJEXL creating a local cache.
+     * @param aJexl the JexlEngine to use.
+     * @param cacheSize the number of expressions in this cache
+     */
+    public UnifiedJEXL(JexlEngine aJexl, int cacheSize) {
+        this.jexl = aJexl;
+        this.cache = aJexl.new SoftCache<String,Expression>(cacheSize);
+    }
+
+    /**
+     * Types of expressions.
+     * Each instance carries a counter index per (composite sub-) expression type.
+     * @see ExpressionBuilder
+     */
+    private static enum ExpressionType {
+        /** Constant expression, count index 0. */
+        CONSTANT(0),
+        /** Immediate expression, count index 1. */
+        IMMEDIATE(1),
+        /** Deferred expression, count index 2. */
+        DEFERRED(2),
+        /** Nested (which are deferred) expressions, count index 2. */
+        NESTED(2),
+        /** Composite expressions are not counted, index -1. */
+        COMPOSITE(-1);
+        /** The index in arrays of expression counters for composite expressions. */
+        private final int index;
+        /**
+         * Creates an ExpressionType.
+         * @param idx the index for this type in counters arrays.
+         */
+        ExpressionType(int idx) {
+            this.index = idx;
+        }
+    }
+
+    /**
+     * A helper class to build expressions.
+     * Keeps count of sub-expressions by type.
+     */
+    private static class ExpressionBuilder {
+        /** Per expression type counters. */
+        private final int[] counts;
+        /** The list of expressions. */
+        private final ArrayList<Expression> expressions;
+
+        /**
+         * Creates a builder.
+         * @param size the initial expression array size
+         */
+        ExpressionBuilder(int size) {
+            counts = new int[]{0, 0, 0};
+            expressions = new ArrayList<Expression>(size <= 0 ? 3 : size);
+        }
+
+        /**
+         * Adds an expression to the list of expressions, maintain per-type counts.
+         * @param expr the expression to add
+         */
+        void add(Expression expr) {
+            counts[expr.getType().index] += 1;
+            expressions.add(expr);
+        }
+
+        /**
+         * Builds an expression from a source, performs checks.
+         * @param el the unified el instance
+         * @param source the source expression
+         * @return an expression
+         */
+        Expression build(UnifiedJEXL el, Expression source) {
+            int sum = 0;
+            for (int count : counts) {
+                sum += count;
+            }
+            if (expressions.size() != sum) {
+                StringBuilder error = new StringBuilder("parsing algorithm error, exprs: ");
+                error.append(expressions.size());
+                error.append(", constant:");
+                error.append(counts[ExpressionType.CONSTANT.index]);
+                error.append(", immediate:");
+                error.append(counts[ExpressionType.IMMEDIATE.index]);
+                error.append(", deferred:");
+                error.append(counts[ExpressionType.DEFERRED.index]);
+                throw new IllegalStateException(error.toString());
+            }
+            // if only one sub-expr, no need to create a composite
+            if (expressions.size() == 1) {
+                return expressions.get(0);
+            } else {
+                return el.new CompositeExpression(counts, expressions, source);
+            }
+        }
+    }
+
+    /**
+     * Gets the JexlEngine underlying the UnifiedJEXL.
+     * @return the JexlEngine
+     */
+    public JexlEngine getEngine() {
+        return jexl;
+    }
+
+    /**
+     * The sole type of (runtime) exception the UnifiedJEXL can throw.
+     */
+    public static class Exception extends RuntimeException {
+        /** Serial version UID. */
+        private static final long serialVersionUID = -8201402995815975726L;
+        /**
+         * Creates a UnifiedJEXL.Exception.
+         * @param msg the exception message
+         * @param cause the exception cause
+         */
+        public Exception(String msg, Throwable cause) {
+            super(msg, cause);
+        }
+    }
+
+    /**
+     * The abstract base class for all expressions, immediate '${...}' and deferred '#{...}'.
+     */
+    public abstract class Expression {
+        /** The source of this expression (see {@link UnifiedJEXL.Expression#prepare}). */
+        protected final Expression source;
+        /**
+         * Creates an expression.
+         * @param src the source expression if any
+         */
+        Expression(Expression src) {
+            this.source = src != null ? src : this;
+        }
+
+        /**
+         * Formats this expression, adding its source string representation in
+         * comments if available: 'expression /*= source *\/'' .
+         * @return the formatted expression string
+         */
+        @Override
+        public String toString() {
+            StringBuilder strb = new StringBuilder();
+            if (source != this) {
+                strb.append(source.toString());
+                strb.append(" /*= ");
+            }
+            asString(strb);
+            if (source != this) {
+                strb.append(" */");
+            }
+            return strb.toString();
+        }
+
+        /**
+         * Generates this expression's string representation.
+         * @return the string representation
+         */
+        public String asString() {
+            StringBuilder strb = new StringBuilder();
+            asString(strb);
+            return strb.toString();
+        }
+
+        /**
+         * Adds this expression's string representation to a StringBuilder.
+         * @param strb the builder to fill
+         */
+        abstract void asString(StringBuilder strb);
+
+        /**
+         * When the expression is dependant upon immediate and deferred sub-expressions,
+         * evaluates the immediate sub-expressions with the context passed as parameter
+         * and returns this expression deferred form.
+         * <p>
+         * In effect, this binds the result of the immediate sub-expressions evaluation in the
+         * context, allowing to differ evaluation of the remaining (deferred) expression within another context.
+         * This only has an effect to nested & composite expressions that contain differed & immediate sub-expressions.
+         * </p>
+         * <p>
+         * If the underlying JEXL engine is silent, errors will be logged through its logger as warning.
+         * </p>
+         * @param context the context to use for immediate expression evaluations
+         * @return  an expression or null if an error occurs and the {@link JexlEngine} is silent
+         * @throws UnifiedJEXL.Exception if an error occurs and the {@link JexlEngine} is not silent
+         */
+        public abstract Expression prepare(JexlContext context);
+
+        /**
+         * Evaluates this expression.
+         * <p>
+         * If the underlying JEXL engine is silent, errors will be logged through its logger as warning.
+         * </p>
+         * @param context the variable context
+         * @return the result of this expression evaluation or null if an error occurs and the {@link JexlEngine} is
+         * silent
+         * @throws UnifiedJEXL.Exception if an error occurs and the {@link JexlEngine} is not silent
+         */
+        public abstract Object evaluate(JexlContext context);
+
+        /**
+         * Checks whether this expression is immediate.
+         * @return true if immediate, false otherwise
+         */
+        public boolean isImmediate() {
+            return true;
+        }
+
+        /**
+         * Checks whether this expression is deferred.
+         * @return true if deferred, false otherwise
+         */
+        public final boolean isDeferred() {
+            return !isImmediate();
+        }
+
+        /**
+         * Retrieves this expression's source expression.
+         * If this expression was prepared, this allows to retrieve the
+         * original expression that lead to it.
+         * Other expressions return themselves.
+         * @return the source expression
+         */
+        public final Expression getSource() {
+            return source;
+        }
+
+        /**
+         * Gets this expression type.
+         * @return its type
+         */
+        abstract ExpressionType getType();
+
+        /**
+         * Prepares a sub-expression for interpretation.
+         * @param interpreter a JEXL interpreter
+         * @return a prepared expression
+         * @throws JexlException (only for nested & composite)
+         */
+        abstract Expression prepare(Interpreter interpreter);
+
+        /**
+         * Intreprets a sub-expression.
+         * @param interpreter a JEXL interpreter
+         * @return the result of interpretation
+         * @throws JexlException (only for nested & composite)
+         */
+        abstract Object evaluate(Interpreter interpreter);
+    }
+
+
+    /** A constant expression. */
+    private class ConstantExpression extends Expression {
+        /** The constant held by this expression. */
+        private final Object value;
+        /**
+         * Creates a constant expression.
+         * <p>
+         * If the wrapped constant is a string, it is treated
+         * as a JEXL strings with respect to escaping.
+         * </p>
+         * @param val the constant value
+         * @param source the source expression if any
+         */
+        ConstantExpression(Object val, Expression source) {
+            super(source);
+            if (val == null) {
+                throw new NullPointerException("constant can not be null");
+            }
+            if (val instanceof String) {
+                val = StringParser.buildString((String) val, false);
+            }
+            this.value = val;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public String asString() {
+            StringBuilder strb = new StringBuilder();
+            strb.append('"');
+            asString(strb);
+            strb.append('"');
+            return strb.toString();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        ExpressionType getType() {
+            return ExpressionType.CONSTANT;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        void asString(StringBuilder strb) {
+            String str = value.toString();
+            if (value instanceof String || value instanceof CharSequence) {
+                for (int i = 0, size = str.length(); i < size; ++i) {
+                    char c = str.charAt(i);
+                    if (c == '"' || c == '\\') {
+                        strb.append('\\');
+                    }
+                    strb.append(c);
+                }
+            } else {
+                strb.append(str);
+            }
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public Expression prepare(JexlContext context) {
+            return this;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        Expression prepare(Interpreter interpreter) {
+            return this;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public Object evaluate(JexlContext context) {
+            return value;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        Object evaluate(Interpreter interpreter) {
+            return value;
+        }
+    }
+
+
+    /** The base for Jexl based expressions. */
+    private abstract class JexlBasedExpression extends Expression {
+        /** The JEXL string for this expression. */
+        protected final CharSequence expr;
+        /** The JEXL node for this expression. */
+        protected final JexlNode node;
+        /**
+         * Creates a JEXL interpretable expression.
+         * @param theExpr the expression as a string
+         * @param theNode the expression as an AST
+         * @param theSource the source expression if any
+         */
+        protected JexlBasedExpression(CharSequence theExpr, JexlNode theNode, Expression theSource) {
+            super(theSource);
+            this.expr = theExpr;
+            this.node = theNode;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public String toString() {
+            StringBuilder strb = new StringBuilder(expr.length() + 3);
+            if (source != this) {
+                strb.append(source.toString());
+                strb.append(" /*= ");
+            }
+            strb.append(isImmediate() ? '$' : '#');
+            strb.append("{");
+            strb.append(expr);
+            strb.append("}");
+            if (source != this) {
+                strb.append(" */");
+            }
+            return strb.toString();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public void asString(StringBuilder strb) {
+            strb.append(isImmediate() ? '$' : '#');
+            strb.append("{");
+            strb.append(expr);
+            strb.append("}");
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public Expression prepare(JexlContext context) {
+            return this;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        Expression prepare(Interpreter interpreter) {
+            return this;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public Object evaluate(JexlContext context) {
+            return UnifiedJEXL.this.evaluate(context, this);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        Object evaluate(Interpreter interpreter) {
+            return interpreter.interpret(node);
+        }
+    }
+
+
+    /** An immediate expression: ${jexl}. */
+    private class ImmediateExpression extends JexlBasedExpression {
+        /**
+         * Creates an immediate expression.
+         * @param expr the expression as a string
+         * @param node the expression as an AST
+         * @param source the source expression if any
+         */
+        ImmediateExpression(CharSequence expr, JexlNode node, Expression source) {
+            super(expr, node, source);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        ExpressionType getType() {
+            return ExpressionType.IMMEDIATE;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public boolean isImmediate() {
+            return true;
+        }
+    }
+
+    /** An immediate expression: ${jexl}. */
+    private class DeferredExpression extends JexlBasedExpression {
+        /**
+         * Creates a deferred expression.
+         * @param expr the expression as a string
+         * @param node the expression as an AST
+         * @param source the source expression if any
+         */
+        DeferredExpression(CharSequence expr, JexlNode node, Expression source) {
+            super(expr, node, source);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        ExpressionType getType() {
+            return ExpressionType.DEFERRED;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public boolean isImmediate() {
+            return false;
+        }
+    }
+
+    /**
+     * A deferred expression that nests an immediate expression.
+     * #{...${jexl}...}
+     * Note that the deferred syntax is JEXL's, not UnifiedJEXL.
+     */
+    private class NestedExpression extends DeferredExpression {
+        /**
+         * Creates a nested expression.
+         * @param expr the expression as a string
+         * @param node the expression as an AST
+         * @param source the source expression if any
+         */
+        NestedExpression(CharSequence expr, JexlNode node, Expression source) {
+            super(expr, node, source);
+            if (this.source != this) {
+                throw new IllegalArgumentException("Nested expression can not have a source");
+            }
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        ExpressionType getType() {
+            return ExpressionType.NESTED;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public String toString() {
+            return expr.toString();
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public Expression prepare(JexlContext context) {
+            return UnifiedJEXL.this.prepare(context, this);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public Expression prepare(Interpreter interpreter) {
+            String value = interpreter.interpret(node).toString();
+            JexlNode dnode = toNode(value, jexl.isDebug()? node.getInfo() : null);
+            return new DeferredExpression(value, dnode, this);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public Object evaluate(Interpreter interpreter) {
+            return prepare(interpreter).evaluate(interpreter);
+        }
+    }
+
+
+    /** A composite expression: "... ${...} ... #{...} ...". */
+    private class CompositeExpression extends Expression {
+        /** Bit encoded (deferred count > 0) bit 1, (immediate count > 0) bit 0. */
+        private final int meta;
+        /** The list of sub-expression resulting from parsing. */
+        private final Expression[] exprs;
+        /**
+         * Creates a composite expression.
+         * @param counters counters of expression per type
+         * @param list the sub-expressions
+         * @param src the source for this expresion if any
+         */
+        CompositeExpression(int[] counters, ArrayList<Expression> list, Expression src) {
+            super(src);
+            this.exprs = list.toArray(new Expression[list.size()]);
+            this.meta = (counters[ExpressionType.DEFERRED.index] > 0 ? 2 : 0)
+                      | (counters[ExpressionType.IMMEDIATE.index] > 0 ? 1 : 0);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        ExpressionType getType() {
+            return ExpressionType.COMPOSITE;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public boolean isImmediate() {
+            // immediate if no deferred
+            return (meta & 2) == 0;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        void asString(StringBuilder strb) {
+            for (Expression e : exprs) {
+                e.asString(strb);
+            }
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public Expression prepare(JexlContext context) {
+            return UnifiedJEXL.this.prepare(context, this);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        Expression prepare(Interpreter interpreter) {
+            // if this composite is not its own source, it is already prepared
+            if (source != this) {
+                return this;
+            }
+            // we need to eval immediate expressions if there are some deferred/nested
+            // ie both immediate & deferred counts > 0, bits 1 & 0 set, (1 << 1) & 1 == 3
+            final boolean evalImmediate = meta == 3;
+            final int size = exprs.length;
+            final ExpressionBuilder builder = new ExpressionBuilder(size);
+            // tracking whether prepare will return a different expression
+            boolean eq = true;
+            for (int e = 0; e < size; ++e) {
+                Expression expr = exprs[e];
+                Expression prepared = expr.prepare(interpreter);
+                if (evalImmediate && prepared instanceof ImmediateExpression) {
+                    // evaluate immediate as constant
+                    Object value = prepared.evaluate(interpreter);
+                    prepared = value == null ? null : new ConstantExpression(value, prepared);
+                }
+                // add it if not null
+                if (prepared != null) {
+                    builder.add(prepared);
+                }
+                // keep track of expression equivalence
+                eq &= expr == prepared;
+            }
+            Expression ready = eq ? this : builder.build(UnifiedJEXL.this, this);
+            return ready;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public Object evaluate(JexlContext context) {
+            return UnifiedJEXL.this.evaluate(context, this);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        Object evaluate(Interpreter interpreter) {
+            final int size = exprs.length;
+            Object value = null;
+            // common case: evaluate all expressions & concatenate them as a string
+            StringBuilder strb = new StringBuilder();
+            for (int e = 0; e < size; ++e) {
+                value = exprs[e].evaluate(interpreter);
+                if (value != null) {
+                    strb.append(value.toString());
+                }
+            }
+            value = strb.toString();
+            return value;
+        }
+    }
+
+    /** Creates a a {@link UnifiedJEXL.Expression} from an expression string.
+     *  Uses & fills up the expression cache if any.
+     * <p>
+     * If the underlying JEXL engine is silent, errors will be logged through its logger as warnings.
+     * </p>
+     * @param expression the UnifiedJEXL string expression
+     * @return the UnifiedJEXL object expression, null if silent and an error occured
+     * @throws UnifiedJEXL.Exception if an error occurs and the {@link JexlEngine} is not silent
+     */
+    public Expression parse(String expression) {
+        Exception xuel = null;
+        Expression stmt = null;
+        try {
+            if (cache == null) {
+                stmt = parseExpression(expression);
+            } else {
+                synchronized (cache) {
+                    stmt = cache.get(expression);
+                    if (stmt == null) {
+                        stmt = parseExpression(expression);
+                        cache.put(expression, stmt);
+                    }
+                }
+            }
+        } catch (JexlException xjexl) {
+            xuel = new Exception("failed to parse '" + expression + "'", xjexl);
+        } catch (Exception xany) {
+            xuel = xany;
+        } finally {
+            if (xuel != null) {
+                if (jexl.isSilent()) {
+                    jexl.logger.warn(xuel.getMessage(), xuel.getCause());
+                    return null;
+                }
+                throw xuel;
+            }
+        }
+        return stmt;
+    }
+
+    /**
+     * Prepares an expression (nested & composites), handles exception reporting.
+     *
+     * @param context the JEXL context to use
+     * @param expr the expression to prepare
+     * @return a prepared expression
+     * @throws UnifiedJEXL.Exception if an error occurs and the {@link JexlEngine} is not silent
+     */
+    Expression prepare(JexlContext context, Expression expr) {
+        try {
+            Interpreter interpreter = jexl.createInterpreter(context);
+            interpreter.setSilent(false);
+            return expr.prepare(interpreter);
+        } catch (JexlException xjexl) {
+            Exception xuel = createException("prepare", expr, xjexl);
+            if (jexl.isSilent()) {
+                jexl.logger.warn(xuel.getMessage(), xuel.getCause());
+                return null;
+            }
+            throw xuel;
+        }
+    }
+
+    /**
+     * Evaluates an expression (nested & composites), handles exception reporting.
+     *
+     * @param context the JEXL context to use
+     * @param expr the expression to prepare
+     * @return the result of the evaluation
+     * @throws UnifiedJEXL.Exception if an error occurs and the {@link JexlEngine} is not silent
+     */
+    Object evaluate(JexlContext context, Expression expr) {
+        try {
+            Interpreter interpreter = jexl.createInterpreter(context);
+            interpreter.setSilent(false);
+            return expr.evaluate(interpreter);
+        } catch (JexlException xjexl) {
+            Exception xuel = createException("evaluate", expr, xjexl);
+            if (jexl.isSilent()) {
+                jexl.logger.warn(xuel.getMessage(), xuel.getCause());
+                return null;
+            }
+            throw xuel;
+        }
+    }
+
+    /**
+     * Use the JEXL parser to create the AST for an expression.
+     * @param expression the expression to parse
+     * @return the AST
+     * @throws JexlException if an error occur during parsing
+     */
+    private JexlNode toNode(CharSequence expression) {
+        return jexl.parse(expression, null);
+    }
+    
+    /**
+     * Use the JEXL parser to create the AST for an expression.
+     * @param expression the expression to parse
+     * @param info debug information
+     * @return the AST
+     * @throws JexlException if an error occur during parsing
+     */
+    private JexlNode toNode(CharSequence expression, JexlInfo info) {
+        return jexl.parse(expression, info);
+    }
+
+    /**
+     * Creates a UnifiedJEXL.Exception from a JexlException.
+     * @param action parse, prepare, evaluate
+     * @param expr the expression
+     * @param xany the exception
+     * @return an exception containing an explicit error message
+     */
+    private Exception createException(String action, Expression expr, java.lang.Exception xany) {
+        StringBuilder strb = new StringBuilder("failed to ");
+        strb.append(action);
+        strb.append(" '");
+        strb.append(expr.toString());
+        strb.append("'");
+        Throwable cause = xany.getCause();
+        if (cause != null) {
+            String causeMsg = cause.getMessage();
+            if (causeMsg != null) {
+                strb.append(", ");
+                strb.append(causeMsg);
+            }
+        }
+        return new Exception(strb.toString(), xany);
+    }
+
+
+    /** The different parsing states. */
+    private static enum ParseState {
+        /** Parsing a constant. */
+        CONST,
+        /** Parsing after $ .*/
+        IMMEDIATE0,
+        /** Parsing after # .*/
+        DEFERRED0,
+        /** Parsing after ${ .*/
+        IMMEDIATE1,
+        /** Parsing after #{ .*/
+        DEFERRED1,
+        /** Parsing after \ .*/
+        ESCAPE
+    }
+
+    /**
+     * Parses a unified expression.
+     * @param expr the string expression
+     * @return the expression instance
+     * @throws JexlException if an error occur during parsing
+     */
+    private Expression parseExpression(String expr) {
+        final int size = expr.length();
+        ExpressionBuilder builder = new ExpressionBuilder(0);
+        StringBuilder strb = new StringBuilder(size);
+        ParseState state = ParseState.CONST;
+        int inner = 0;
+        boolean nested = false;
+        int inested = -1;
+        for (int i = 0; i < size; ++i) {
+            char c = expr.charAt(i);
+            switch (state) {
+                default: // in case we ever add new expression type
+                    throw new UnsupportedOperationException("unexpected expression type");
+                case CONST:
+                    if (c == '$') {
+                        state = ParseState.IMMEDIATE0;
+                    } else if (c == '#') {
+                        inested = i;
+                        state = ParseState.DEFERRED0;
+                    } else if (c == '\\') {
+                        state = ParseState.ESCAPE;
+                    } else {
+                        // do buildup expr
+                        strb.append(c);
+                    }
+                    break;
+                case IMMEDIATE0: // $
+                    if (c == '{') {
+                        state = ParseState.IMMEDIATE1;
+                        // if chars in buffer, create constant
+                        if (strb.length() > 0) {
+                            Expression cexpr = new ConstantExpression(strb.toString(), null);
+                            builder.add(cexpr);
+                            strb.delete(0, Integer.MAX_VALUE);
+                        }
+                    } else {
+                        // revert to CONST
+                        strb.append('$');
+                        strb.append(c);
+                        state = ParseState.CONST;
+                    }
+                    break;
+                case DEFERRED0: // #
+                    if (c == '{') {
+                        state = ParseState.DEFERRED1;
+                        // if chars in buffer, create constant
+                        if (strb.length() > 0) {
+                            Expression cexpr = new ConstantExpression(strb.toString(), null);
+                            builder.add(cexpr);
+                            strb.delete(0, Integer.MAX_VALUE);
+                        }
+                    } else {
+                        // revert to CONST
+                        strb.append('#');
+                        strb.append(c);
+                        state = ParseState.CONST;
+                    }
+                    break;
+                case IMMEDIATE1: // ${...
+                    if (c == '}') {
+                        // materialize the immediate expr
+                        Expression iexpr = new ImmediateExpression(strb.toString(), toNode(strb), null);
+                        builder.add(iexpr);
+                        strb.delete(0, Integer.MAX_VALUE);
+                        state = ParseState.CONST;
+                    } else {
+                        // do buildup expr
+                        strb.append(c);
+                    }
+                    break;
+                case DEFERRED1: // #{...
+                    // skip inner strings (for '}')
+                    if (c == '"' || c == '\'') {
+                        strb.append(c);
+                        i = StringParser.readString(strb, expr, i + 1, c);
+                        continue;
+                    }
+                    // nested immediate in deferred; need to balance count of '{' & '}'
+                    if (c == '{') {
+                        if (expr.charAt(i - 1) == '$') {
+                            inner += 1;
+                            strb.deleteCharAt(strb.length() - 1);
+                            nested = true;
+                        }
+                        continue;
+                    }
+                    // closing '}'
+                    if (c == '}') {
+                        // balance nested immediate
+                        if (inner > 0) {
+                            inner -= 1;
+                        } else {
+                            // materialize the nested/deferred expr
+                            Expression dexpr = null;
+                            if (nested) {
+                                dexpr = new NestedExpression(expr.substring(inested, i + 1), toNode(strb), null);
+                            } else {
+                                dexpr = new DeferredExpression(strb.toString(), toNode(strb), null);
+                            }
+                            builder.add(dexpr);
+                            strb.delete(0, Integer.MAX_VALUE);
+                            nested = false;
+                            state = ParseState.CONST;
+                        }
+                    } else {
+                        // do buildup expr
+                        strb.append(c);
+                    }
+                    break;
+                case ESCAPE:
+                    if (c == '#') {
+                        strb.append('#');
+                    } else if (c == '$') {
+                        strb.append('$');
+                    } else {
+                        strb.append('\\');
+                        strb.append(c);
+                    }
+                    state = ParseState.CONST;
+            }
+        }
+        // we should be in that state
+        if (state != ParseState.CONST) {
+            throw new Exception("malformed expression: " + expr, null);
+        }
+        // if any chars were buffered, add them as a constant
+        if (strb.length() > 0) {
+            Expression cexpr = new ConstantExpression(strb.toString(), null);
+            builder.add(cexpr);
+        }
+        return builder.build(this, null);
+    }
+}
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/AbstractExecutor.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/AbstractExecutor.java
new file mode 100644
index 0000000..f945597
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/AbstractExecutor.java
@@ -0,0 +1,378 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2.internal;
+import org.apache.commons.jexl2.internal.introspection.MethodKey;
+import org.apache.commons.jexl2.introspection.JexlMethod;
+import org.apache.commons.jexl2.introspection.JexlPropertySet;
+import org.apache.commons.jexl2.introspection.JexlPropertyGet;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Abstract class that is used to execute an arbitrary
+ * method that is introspected. This is the superclass
+ * for all other AbstractExecutor classes.
+ *
+ * @since 1.0
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
+ * @version $Id$
+ */
+public abstract class AbstractExecutor {
+    /** A marker for invocation failures in tryInvoke. */
+    public static final Object TRY_FAILED = new Object() {
+        @Override
+        public String toString() {
+            return "tryExecute failed";
+        }
+    };
+
+    /**
+     * A helper to initialize the marker methods (array.get, list.get, etc...).
+     * @param clazz the class to introspect
+     * @param name the name of the method
+     * @param parms the parameters
+     * @return the method
+     */
+    static java.lang.reflect.Method initMarker(Class<?> clazz, String name, Class<?>... parms) {
+        try {
+            return clazz.getMethod(name, parms);
+        } catch (Exception xnever) {
+            throw new Error(xnever);
+        }
+    }
+
+    /**
+     * Creates an arguments array.
+     * @param args the list of arguments
+     * @return the arguments array
+     */
+    static Object[] makeArgs(Object... args) {
+        return args;
+    }
+
+    /** The class this executor applies to. */
+    protected final Class<?> objectClass;
+    /** Method to be executed. */
+    protected final java.lang.reflect.Method method;
+
+    /**
+     * Default and sole constructor.
+     * @param theClass the class this executor applies to
+     * @param theMethod the method held by this executor
+     */
+    protected AbstractExecutor(Class<?> theClass, java.lang.reflect.Method theMethod) {
+        objectClass = theClass;
+        method = theMethod;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object obj) {
+        return this == obj || (obj instanceof AbstractExecutor && equals((AbstractExecutor) obj));
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return method.hashCode();
+    }
+
+    /**
+     *  Indicates whether some other executor is equivalent to this one.
+     * @param arg the other executor to check
+     * @return true if both executors are equivalent, false otherwise
+     */
+    public boolean equals(AbstractExecutor arg) {
+        // common equality check
+        if (!this.getClass().equals(arg.getClass())) {
+            return false;
+        }
+        if (!this.getMethod().equals(arg.getMethod())) {
+            return false;
+        }
+        if (!this.getTargetClass().equals(arg.getTargetClass())) {
+            return false;
+        }
+        // specific equality check
+        Object lhsp = this.getTargetProperty();
+        Object rhsp = arg.getTargetProperty();
+        if (lhsp == null && rhsp == null) {
+            return true;
+        }
+        if (lhsp != null && rhsp != null) {
+            return lhsp.equals(rhsp);
+        }
+        return false;
+    }
+
+    /**
+     * Tell whether the executor is alive by looking
+     * at the value of the method.
+     *
+     * @return boolean Whether the executor is alive.
+     */
+    public final boolean isAlive() {
+        return (method != null);
+    }
+
+    /**
+     * Specifies if this executor is cacheable and able to be reused for this
+     * class of object it was returned for.
+     *
+     * @return true if can be reused for this class, false if not
+     */
+    public boolean isCacheable() {
+        return method != null;
+    }
+
+    /**
+     * Gets the method to be executed or used as a marker.
+     * @return Method The method used by execute in derived classes.
+     */
+    public final java.lang.reflect.Method getMethod() {
+        return method;
+    }
+
+    /**
+     * Gets the object class targeted by this executor.
+     * @return the target object class
+     */
+    public final Class<?> getTargetClass() {
+        return objectClass;
+    }
+    
+    /**
+     * Gets the property targeted by this executor.
+     * @return the target property
+     */
+    public Object getTargetProperty() {
+        return null;
+    }
+
+    /**
+     * Gets the method name used.
+     * @return method name
+     */
+    public final String getMethodName() {
+        return method.getName();
+    }
+
+
+    /**
+     * Checks whether a tryExecute failed or not.
+     * @param exec the value returned by tryExecute
+     * @return true if tryExecute failed, false otherwise
+     */
+    public final boolean tryFailed(Object exec) {
+        return exec == TRY_FAILED;
+    }
+
+    /**
+     * Abstract class that is used to execute an arbitrary 'get' method.
+     */
+    public abstract static class Get extends AbstractExecutor implements JexlPropertyGet {
+        /**
+         * Default and sole constructor.
+         * @param theClass the class this executor applies to
+         * @param theMethod the method held by this executor
+         */
+        protected Get(Class<?> theClass, java.lang.reflect.Method theMethod) {
+            super(theClass, theMethod);
+        }
+
+        /** {@inheritDoc} */
+        public final Object invoke(Object obj) throws Exception {
+            return execute(obj);
+        }
+        
+        /** {@inheritDoc} */
+        public final Object tryInvoke(Object obj, Object key) {
+            return tryExecute(obj, key);
+        }
+
+        /**
+         * Gets the property value from an object.
+         *
+         * @param obj The object to get the property from.
+         * @return The property value.
+         * @throws IllegalAccessException Method is inaccessible.
+         * @throws InvocationTargetException Method body throws an exception.
+         */
+        public abstract Object execute(Object obj)
+                throws IllegalAccessException, InvocationTargetException;
+
+        /**
+         * Tries to reuse this executor, checking that it is compatible with
+         * the actual set of arguments.
+         * <p>Compatibility means that:
+         * <code>o</code> must be of the same class as this executor's
+         * target class and
+         * <code>property</code> must be of the same class as this
+         * executor's target property (for list and map based executors) and have the same
+         * value (for other types).</p>
+         * @param obj The object to get the property from.
+         * @param key The property to get from the object.
+         * @return The property value or TRY_FAILED if checking failed.
+         */
+        public Object tryExecute(Object obj, Object key) {
+            return TRY_FAILED;
+        }
+    }
+    
+    /**
+     * Abstract class that is used to execute an arbitrary 'set' method.
+     */
+    public abstract static class Set extends AbstractExecutor implements JexlPropertySet {
+        /**
+         * Default and sole constructor.
+         * @param theClass the class this executor applies to
+         * @param theMethod the method held by this executor
+         */
+        protected Set(Class<?> theClass, java.lang.reflect.Method theMethod) {
+            super(theClass, theMethod);
+        }
+
+        /** {@inheritDoc} */
+        public final Object invoke(Object obj, Object arg) throws Exception {
+            return execute(obj, arg);
+        }
+
+        /** {@inheritDoc} */
+        public final Object tryInvoke(Object obj, Object key, Object value) {
+            return tryExecute(obj, key, value);
+        }
+
+        /**
+         * Sets the property value of an object.
+         *
+         * @param obj The object to set the property in.
+         * @param value The value.
+         * @return The return value.
+         * @throws IllegalAccessException Method is inaccessible.
+         * @throws InvocationTargetException Method body throws an exception.
+         */
+        public abstract Object execute(Object obj, Object value)
+                throws IllegalAccessException, InvocationTargetException;
+
+        /**
+         * Tries to reuse this executor, checking that it is compatible with
+         * the actual set of arguments.
+         * <p>Compatibility means that:
+         * <code>o</code> must be of the same class as this executor's
+         * target class,
+         * <code>property</code> must be of the same class as this
+         * executor's target property (for list and map based executors) and have the same
+         * value (for other types)
+         * and that <code>arg</code> must be a valid argument for this
+         * executor underlying method.</p>
+         * @param obj The object to invoke the method from.
+         * @param key The property to set in the object.
+         * @param value The value to use as the property value.
+         * @return The return value or TRY_FAILED if checking failed.
+         */
+        public Object tryExecute(Object obj, Object key, Object value) {
+            return TRY_FAILED;
+        }
+        
+    }
+
+
+
+    /**
+     * Abstract class that is used to execute an arbitrary method.
+     */
+    public abstract static class Method extends AbstractExecutor implements JexlMethod {
+        /**
+         * A helper class to pass the method &amp; parameters.
+         */
+        protected static final class Parameter {
+            /** The method. */
+            private final java.lang.reflect.Method method;
+            /** The method key. */
+            private final MethodKey key;
+            /** Creates an instance.
+             * @param m the method
+             * @param k the method key
+             */
+            public Parameter(java.lang.reflect.Method m, MethodKey k) {
+                method = m;
+                key = k;
+            }
+        }
+        /** The method key discovered from the arguments. */
+        protected final MethodKey key;
+        /**
+         * Creates a new instance.
+         * @param c the class this executor applies to
+         * @param km the method and MethodKey to encapsulate.
+         */
+        protected Method(Class<?> c, Parameter km) {
+            super(c, km.method);
+            key = km.key;
+        }
+
+        /** {@inheritDoc} */
+        public final Object invoke(Object obj, Object[] params) throws Exception {
+            return execute(obj, params);
+        }
+
+        /** {@inheritDoc} */
+        public final Object tryInvoke(String name, Object obj, Object[] params) {
+            return tryExecute(name, obj, params);
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public Object getTargetProperty() {
+            return key;
+        }
+        
+        /**
+         * Returns the return type of the method invoked.
+         * @return return type
+         */
+        public final Class<?> getReturnType() {
+            return method.getReturnType();
+        }
+
+        /**
+         * Invokes the method to be executed.
+         *
+         * @param obj the object to invoke the method upon
+         * @param args the method arguments
+         * @return the result of the method invocation
+         * @throws IllegalAccessException Method is inaccessible.
+         * @throws InvocationTargetException Method body throws an exception.
+         */
+        public abstract Object execute(Object obj, Object[] args)
+                throws IllegalAccessException, InvocationTargetException;
+
+        /**
+         * Tries to reuse this executor, checking that it is compatible with
+         * the actual set of arguments.
+         * @param obj the object to invoke the method upon
+         * @param name the method name
+         * @param args the method arguments
+         * @return the result of the method invocation or TRY_FAILED if checking failed.
+         */
+        public Object tryExecute(String name, Object obj, Object[] args){
+            return TRY_FAILED;
+        }
+
+    }
+
+}
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/ArrayIterator.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/ArrayIterator.java
new file mode 100644
index 0000000..35f1b35
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/ArrayIterator.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2.internal;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.lang.reflect.Array;
+
+/**
+ *  <p>
+ *  An Iterator wrapper for an Object[]. This will
+ *  allow us to deal with all array like structures
+ *  in a consistent manner.
+ *  </p>
+ *  <p>
+ *  WARNING : this class's operations are NOT synchronized.
+ *  It is meant to be used in a single thread, newly created
+ *  for each use in the #foreach() directive.
+ *  If this is used or shared, synchronize in the
+ *  next() method.
+ *  </p>
+ *
+ * @since 1.0
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
+ * @version $Id$
+ */
+public class ArrayIterator implements Iterator<Object> {
+    /** The objects to iterate over. */
+    private final Object array;
+    /** The size of the array. */
+    private final int size;
+    /** The current position and size in the array. */
+    private int pos;
+
+    /**
+     * Creates a new iterator instance for the specified array.
+     * @param arr The array for which an iterator is desired.
+     */
+    public ArrayIterator(Object arr) {
+        if (arr == null) {
+            array = null;
+            pos = 0;
+            size = 0;
+        } else if (!arr.getClass().isArray()) {
+            throw new IllegalArgumentException(arr.getClass() + " is not an array");
+        } else {
+            array = arr;
+            pos = 0;
+            size = Array.getLength(array);
+        }
+    }
+
+    /**
+     * Move to next element in the array.
+     *
+     * @return The next object in the array.
+     */
+    public Object next() {
+        if (pos < size) {
+            return Array.get(array, pos++);
+        }    
+        // we screwed up...
+        throw new NoSuchElementException("No more elements: " + pos
+                                         + " / " + size);
+    }
+    
+    /**
+     * Check to see if there is another element in the array.
+     *
+     * @return Whether there is another element.
+     */
+    public boolean hasNext() {
+        return (pos < size);
+    }
+
+    /**
+     * No op--merely added to satify the <code>Iterator</code> interface.
+     */
+    public void remove() {
+        throw new UnsupportedOperationException();
+    }
+}
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/ArrayListWrapper.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/ArrayListWrapper.java
new file mode 100644
index 0000000..ad1ad39
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/ArrayListWrapper.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2.internal;
+
+import java.lang.reflect.Array;
+import java.util.AbstractList;
+
+/**
+ * A class that wraps an array with a List interface.
+ * 
+ * @author Chris Schultz &lt;chris@christopherschultz.net$gt;
+ * @version $Revision$ $Date: 2006-04-14 19:40:41 $
+ */
+public class ArrayListWrapper extends AbstractList<Object> {
+    /** the array to wrap. */
+    private final Object array;
+
+    /**
+     * Create the wrapper.
+     * @param anArray {@link #array}
+     */
+    public ArrayListWrapper(Object anArray) {
+        if (!anArray.getClass().isArray()) {
+            throw new IllegalArgumentException(anArray.getClass() + " is not an array");
+        }
+        this.array = anArray;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object get(int index) {
+        return Array.get(array, index);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object set(int index, Object element) {
+        Object old = get(index);
+        Array.set(array, index, element);
+        return old;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int size() {
+        return Array.getLength(array);
+    }
+}
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/BooleanGetExecutor.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/BooleanGetExecutor.java
new file mode 100644
index 0000000..f758193
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/BooleanGetExecutor.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2.internal;
+import java.lang.reflect.InvocationTargetException;
+/**
+ * Specialized executor to get a boolean property from an object.
+ * @since 2.0
+ */
+public final class BooleanGetExecutor extends AbstractExecutor.Get {
+    /** The property. */
+    private final String property;
+    /**
+     * Creates an instance by attempting discovery of the get method.
+     * @param is the introspector
+     * @param clazz the class to introspect
+     * @param key the property to get
+     */
+    public BooleanGetExecutor(Introspector is, Class<?> clazz, String key) {
+        super(clazz, discover(is, clazz, key));
+        property = key;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object getTargetProperty() {
+        return property;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object execute(Object obj)
+        throws IllegalAccessException, InvocationTargetException {
+        return method == null ? null : method.invoke(obj, (Object[]) null);
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    public Object tryExecute(Object obj, Object key) {
+        if (obj != null && method !=  null
+            // ensure method name matches the property name
+            && property.equals(key)
+            && objectClass.equals(obj.getClass())) {
+            try {
+                return method.invoke(obj, (Object[]) null);
+            } catch (InvocationTargetException xinvoke) {
+                return TRY_FAILED; // fail
+            } catch (IllegalAccessException xill) {
+                return TRY_FAILED;// fail
+            }
+        }
+        return TRY_FAILED;
+    }
+
+    /**
+     * Discovers the method for a {@link BooleanGet}.
+     * <p>The method to be found should be named "is{P,p}property and return a boolean.</p>
+     *@param is the introspector
+     *@param clazz the class to find the get method from
+     *@param property the the property name
+     *@return the method if found, null otherwise
+     */
+    static java.lang.reflect.Method discover(Introspector is, final Class<?> clazz, String property) {
+        java.lang.reflect.Method m = PropertyGetExecutor.discoverGet(is, "is", clazz, property);
+        return (m != null && m.getReturnType() == Boolean.TYPE) ? m : null;
+    }
+
+}
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/DuckGetExecutor.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/DuckGetExecutor.java
new file mode 100644
index 0000000..3b1cbf9
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/DuckGetExecutor.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2.internal;
+
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Specialized executor to get a property from an object.
+ * <p>Duck as in duck-typing for an interface like:
+ * <code>
+ * interface Get {
+ *      Object get(Object key);
+ * }
+ * </code>
+ * </p>
+ * @since 2.0
+ */
+public final class DuckGetExecutor extends AbstractExecutor.Get {
+    /** The property. */
+    private final Object property;
+
+    /**
+     * Creates an instance by attempting discovery of the get method.
+     * @param is the introspector
+     * @param clazz the class to introspect
+     * @param identifier the property to get
+     */
+    public DuckGetExecutor(Introspector is, Class<?> clazz, Object identifier) {
+        super(clazz, discover(is, clazz, identifier));
+        property = identifier;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object getTargetProperty() {
+        return property;
+    }
+
+    /**
+     * Get the property from the object.
+     * @param obj the object.
+     * @return object.get(property)
+     * @throws IllegalAccessException Method is inaccessible.
+     * @throws InvocationTargetException Method body throws an exception.
+     */
+    @Override
+    public Object execute(Object obj)
+            throws IllegalAccessException, InvocationTargetException {
+        Object[] args = {property};
+        return method == null ? null : method.invoke(obj, args);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object tryExecute(Object obj, Object key) {
+        if (obj != null && method !=  null
+            // ensure method name matches the property name
+            && property.equals(key)
+            && objectClass.equals(obj.getClass())) {
+            try {
+                Object[] args = {property};
+                return method.invoke(obj, args);
+            } catch (InvocationTargetException xinvoke) {
+                return TRY_FAILED; // fail
+            } catch (IllegalAccessException xill) {
+                return TRY_FAILED;// fail
+            }
+        }
+        return TRY_FAILED;
+    }
+
+    /**
+     * Discovers a method for a {@link GetExecutor.DuckGet}.
+     *@param is the introspector
+     *@param clazz the class to find the get method from
+     *@param identifier the key to use as an argument to the get method
+     *@return the method if found, null otherwise
+     */
+    private static java.lang.reflect.Method discover(Introspector is,
+            final Class<?> clazz, Object identifier) {
+        return is.getMethod(clazz, "get", makeArgs(identifier));
+    }
+}
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/DuckSetExecutor.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/DuckSetExecutor.java
new file mode 100644
index 0000000..3ec9925
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/DuckSetExecutor.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2.internal;
+
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Specialized executor to set a property of an object.
+ * <p>Duck as in duck-typing for an interface like:
+ * <code>
+ * interface Set {
+ *      Object set(Object property, Object value);
+ * }
+ * </code>
+ * </p>
+ * @since 2.0
+ */
+public final class DuckSetExecutor extends AbstractExecutor.Set {
+    /** The property. */
+    private final Object property;
+    
+    /**
+     * Creates an instance.
+     *@param is the introspector
+     *@param clazz the class to find the set method from
+     *@param key the key to use as 1st argument to the set method
+     *@param value the value to use as 2nd argument to the set method
+     */
+    public DuckSetExecutor(Introspector is, Class<?> clazz, Object key, Object value) {
+        super(clazz, discover(is, clazz, key, value));
+        property = key;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object getTargetProperty() {
+        return property;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object execute(Object obj, Object value)
+            throws IllegalAccessException, InvocationTargetException {
+        Object[] pargs = {property, value};
+        if (method != null) {
+            method.invoke(obj, pargs);
+        }
+        return value;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object tryExecute(Object obj, Object key, Object value) {
+        if (obj != null && method !=  null
+            // ensure method name matches the property name
+            && property.equals(key)
+            && objectClass.equals(obj.getClass())) {
+            try {
+                Object[] args = {property, value};
+                method.invoke(obj, args);
+                return value;
+            } catch (InvocationTargetException xinvoke) {
+                return TRY_FAILED; // fail
+            } catch (IllegalAccessException xill) {
+                return TRY_FAILED;// fail
+            }
+        }
+        return TRY_FAILED;
+    }
+
+    /**
+     * Discovers the method for a {@link DuckSet}.
+     *@param is the introspector
+     *@param clazz the class to find the set method from
+     *@param key the key to use as 1st argument to the set method
+     *@param value the value to use as 2nd argument to the set method
+     *@return the method if found, null otherwise
+     */
+    private static java.lang.reflect.Method discover(Introspector is,
+            Class<?> clazz, Object key, Object value) {
+        return is.getMethod(clazz, "set", makeArgs(key, value));
+    }
+}
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/EnumerationIterator.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/EnumerationIterator.java
new file mode 100644
index 0000000..91c22a4
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/EnumerationIterator.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2.internal;
+
+
+import java.util.Iterator;
+import java.util.Enumeration;
+
+/**
+ * An Iterator wrapper for an Enumeration.
+ * @since 1.0
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @version $Id$
+ * @param <T> the type of object this iterator returns
+ */
+public class EnumerationIterator<T> implements Iterator<T> {
+    /**
+     * The enumeration to iterate over.
+     */
+    private final Enumeration<T> enumeration;
+
+    /**
+     * Creates a new iteratorwrapper instance for the specified 
+     * Enumeration.
+     *
+     * @param enumer  The Enumeration to wrap.
+     */
+    public EnumerationIterator(Enumeration<T> enumer) {
+        enumeration = enumer;
+    }
+
+    /**
+     * Move to next element in the array.
+     *
+     * @return The next object in the array.
+     */
+    public T next() {
+        return enumeration.nextElement();
+    }
+    
+    /**
+     * Check to see if there is another element in the array.
+     *
+     * @return Whether there is another element.
+     */
+    public boolean hasNext() {
+        return enumeration.hasMoreElements();
+    }
+
+    /**
+     *  Unimplemented.  No analogy in Enumeration
+     */
+    public void remove() {
+        // not implemented
+    }
+}
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/Introspector.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/Introspector.java
new file mode 100644
index 0000000..f3ce3f1
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/Introspector.java
@@ -0,0 +1,286 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2.internal;
+
+import java.lang.ref.SoftReference;
+import java.lang.reflect.Method;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+
+import org.apache.commons.jexl2.internal.introspection.IntrospectorBase;
+import org.apache.commons.jexl2.internal.introspection.MethodKey;
+
+import org.apache.commons.logging.Log;
+
+/**
+ *  Default introspection services.
+ *  <p>Finding methods as well as property getters & setters.</p>
+ * @since 1.0
+ */
+public class Introspector {
+    /** The logger to use for all warnings & errors. */
+    protected final Log rlog;
+    /** The soft reference to the introspector currently in use. */
+    private volatile SoftReference<IntrospectorBase> ref;
+    
+    /**
+     * Creates an introspector.
+     * @param log the logger to use for warnings.
+     */
+    protected Introspector(Log log) {
+        rlog = log;
+        ref = new SoftReference<IntrospectorBase>(null);
+    }
+
+    /**
+     * Coerce an Object  to an Integer.
+     * @param arg the Object to coerce
+     * @return an Integer if it can be converted, null otherwise
+     */
+    protected Integer toInteger(Object arg) {
+        if (arg == null) {
+            return null;
+        }
+        if (arg instanceof Number) {
+            return Integer.valueOf(((Number) arg).intValue());
+        }
+        try {
+            return Integer.valueOf(arg.toString());
+        } catch (NumberFormatException xnumber) {
+            return null;
+        }
+    }
+
+    /**
+     * Coerce an Object to a String.
+     * @param arg the Object to coerce
+     * @return a String if it can be converted, null otherwise
+     */
+    protected String toString(Object arg) {
+        return arg == null ? null : arg.toString();
+    }
+
+    /**
+     * Gets the current introspector base.
+     * <p>If the reference has been collected, this method will recreate the underlying introspector.</p>
+     * @return the introspector
+     */
+    // CSOFF: DoubleCheckedLocking
+    protected final IntrospectorBase base() {
+        IntrospectorBase intro = ref.get();
+        if (intro == null) {
+            // double checked locking (fixed by Java 5 memory model).
+            synchronized(this) {
+                intro = ref.get();
+                if (intro == null) {
+                    intro = new IntrospectorBase(rlog);
+                    ref = new SoftReference<IntrospectorBase>(intro);
+                }
+            }
+        }
+        return intro;
+    }
+    // CSON: DoubleCheckedLocking
+
+    /**
+     * Sets the underlying class loader for class solving resolution.
+     * @param loader the loader to use
+     */
+    public void setClassLoader(ClassLoader loader) {
+        base().setLoader(loader);
+    }
+
+
+    /**
+     * Gets the field named by <code>key</code> for the class <code>c</code>.
+     *
+     * @param c     Class in which the field search is taking place
+     * @param key   Name of the field being searched for
+     * @return the desired field or null if it does not exist or is not accessible
+     * */
+    protected final Field getField(Class<?> c, String key) {
+        return base().getField(c, key);
+    }
+
+    /**
+     * Gets the accessible field names known for a given class.
+     * @param c the class
+     * @return the class field names
+     */
+    public final String[] getFieldNames(Class<?> c) {
+        return base().getFieldNames(c);
+    }
+
+    /**
+     * Gets the method defined by <code>name</code> and
+     * <code>params</code> for the Class <code>c</code>.
+     *
+     * @param c Class in which the method search is taking place
+     * @param name Name of the method being searched for
+     * @param params An array of Objects (not Classes) that describe the
+     *               the parameters
+     *
+     * @return The desired Method object.
+     * @throws IllegalArgumentException When the parameters passed in can not be used for introspection.
+     * CSOFF: RedundantThrows
+     */
+    protected final Method getMethod(Class<?> c, String name, Object[] params) throws IllegalArgumentException {
+        return base().getMethod(c, new MethodKey(name, params));
+    }
+
+    /**
+     * Gets the method defined by <code>key</code> and for the Class <code>c</code>.
+     *
+     * @param c Class in which the method search is taking place
+     * @param key MethodKey of the method being searched for
+     *
+     * @return The desired Method object.
+     * @throws IllegalArgumentException When the parameters passed in can not be used for introspection.
+     * CSOFF: RedundantThrows
+     */
+    protected final Method getMethod(Class<?> c, MethodKey key) throws IllegalArgumentException {
+        return base().getMethod(c, key);
+    }
+
+
+    /**
+     * Gets the accessible methods names known for a given class.
+     * @param c the class
+     * @return the class method names
+     */
+    public final String[] getMethodNames(Class<?> c) {
+        return base().getMethodNames(c);
+    }
+
+    /**
+     * Returns a general constructor.
+     * @param ctorHandle the object
+     * @param args contrusctor arguments
+     * @return a {@link java.lang.reflect.Constructor}
+     */
+    public final Constructor<?> getConstructor(Object ctorHandle, Object[] args) {
+        String className = null;
+        Class<?> clazz = null;
+        if (ctorHandle instanceof Class<?>) {
+            clazz = (Class<?>) ctorHandle;
+            className = clazz.getName();
+        } else if (ctorHandle != null) {
+            className = ctorHandle.toString();
+        } else {
+            return null;
+        }
+        return base().getConstructor(clazz, new MethodKey(className, args));
+    }
+
+    /**
+     * Returns a general method.
+     * @param obj the object
+     * @param name the method name
+     * @param args method arguments
+     * @return a {@link AbstractExecutor.Method}.
+     */
+    public final AbstractExecutor.Method getMethodExecutor(Object obj, String name, Object[] args) {
+        AbstractExecutor.Method me = new MethodExecutor(this, obj, name, args);
+        return me.isAlive() ? me : null;
+    }
+
+    /**
+     * Return a property getter.
+     * @param obj the object to base the property from.
+     * @param identifier property name
+     * @return a {@link AbstractExecutor.Get}.
+     */
+    public final AbstractExecutor.Get getGetExecutor(Object obj, Object identifier) {
+        final Class<?> claz = obj.getClass();
+        final String property = toString(identifier);
+        AbstractExecutor.Get executor;
+        // first try for a getFoo() type of property (also getfoo() )
+        if (property != null) {
+            executor = new PropertyGetExecutor(this, claz, property);
+            if (executor.isAlive()) {
+                return executor;
+            }
+        }
+        // look for boolean isFoo()
+        if (property != null) {
+            executor = new BooleanGetExecutor(this, claz, property);
+            if (executor.isAlive()) {
+                return executor;
+            }
+        }
+        // let's see if we are a map...
+        executor = new MapGetExecutor(this, claz, identifier);
+        if (executor.isAlive()) {
+            return executor;
+        }
+        // let's see if we can convert the identifier to an int,
+        // if obj is an array or a list, we can still do something
+        Integer index = toInteger(identifier);
+        if (index != null) {
+            executor = new ListGetExecutor(this, claz, index);
+            if (executor.isAlive()) {
+                return executor;
+            }
+        }
+        // if that didn't work, look for set("foo")
+        executor = new DuckGetExecutor(this, claz, identifier);
+        if (executor.isAlive()) {
+            return executor;
+        }
+        return null;
+    }
+
+    /**
+     * Return a property setter.
+     * @param obj the object to base the property from.
+     * @param identifier property name (or identifier)
+     * @param arg value to set
+     * @return a {@link AbstractExecutor.Set}.
+     */
+    public final AbstractExecutor.Set getSetExecutor(final Object obj, final Object identifier, Object arg) {
+        final Class<?> claz = obj.getClass();
+        final String property = toString(identifier);
+        AbstractExecutor.Set executor;
+        // first try for a setFoo() type of property (also setfoo() )
+        if (property != null) {
+            executor = new PropertySetExecutor(this, claz, property, arg);
+            if (executor.isAlive()) {
+                return executor;
+            }
+        }
+        // let's see if we are a map...
+        executor = new MapSetExecutor(this, claz, identifier, arg);
+        if (executor.isAlive()) {
+            return executor;
+        }
+        // let's see if we can convert the identifier to an int,
+        // if obj is an array or a list, we can still do something
+        Integer index = toInteger(identifier);
+        if (index != null) {
+            executor = new ListSetExecutor(this, claz, index, arg);
+            if (executor.isAlive()) {
+                return executor;
+            }
+        }
+        // if that didn't work, look for set("foo")
+        executor = new DuckSetExecutor(this, claz, property, arg);
+        if (executor.isAlive()) {
+            return executor;
+        }
+        return null;
+    }
+}
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/ListGetExecutor.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/ListGetExecutor.java
new file mode 100644
index 0000000..253f0c6
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/ListGetExecutor.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2.internal;
+import java.util.List;
+import java.lang.reflect.Array;
+/**
+ * Specialized executor to get a property from a List or array.
+ * @since 2.0
+ */
+public final class ListGetExecutor extends AbstractExecutor.Get {
+    /** The java.lang.reflect.Array.get method used as an active marker in ListGet. */
+    private static final java.lang.reflect.Method ARRAY_GET =
+            initMarker(Array.class, "get", Object.class, Integer.TYPE);
+    /** The java.util.obj.get method used as an active marker in ListGet. */
+    private static final java.lang.reflect.Method LIST_GET =
+            initMarker(List.class, "get", Integer.TYPE);
+    /** The property. */
+    private final Integer property;
+
+    /**
+     * Creates an instance checking for the List interface or Array capability.
+     * @param is the introspector
+     * @param clazz the class to introspect
+     * @param key the key to use in obj.get(key)
+     */
+    public ListGetExecutor(Introspector is, Class<?> clazz, Integer key) {
+        super(clazz, discover(clazz));
+        property = key;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object getTargetProperty() {
+        return property;
+    }
+    
+    /**
+     * Get the property from the obj or array.
+     * @param obj the List/array.
+     * @return obj.get(key)
+     */
+    @Override
+    public Object execute(final Object obj) {
+        if (method == ARRAY_GET) {
+            return java.lang.reflect.Array.get(obj, property.intValue());
+        } else {
+            return ((List<?>) obj).get(property.intValue());
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object tryExecute(final Object obj, Object key) {
+        if (obj != null && method != null
+            && objectClass.equals(obj.getClass())
+            && key instanceof Integer) {
+            if (method == ARRAY_GET) {
+                return java.lang.reflect.Array.get(obj, ((Integer) key).intValue());
+            } else {
+                return ((List<?>) obj).get(((Integer) key).intValue());
+            }
+        }
+        return TRY_FAILED;
+    }
+
+
+    /**
+     * Finds the method to perform the get on a obj of array.
+     * @param clazz the class to introspect
+     * @return a marker method, obj.get or array.get
+     */
+    static java.lang.reflect.Method discover(Class<?> clazz) {
+        //return discoverList(false, clazz, property);
+        if (clazz.isArray()) {
+            return ARRAY_GET;
+        }
+        if (List.class.isAssignableFrom(clazz)) {
+            return LIST_GET;
+        }
+        return null;
+    }
+}
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/ListSetExecutor.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/ListSetExecutor.java
new file mode 100644
index 0000000..42896fd
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/ListSetExecutor.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2.internal;
+import java.util.List;
+import java.lang.reflect.Array;
+/**
+ * Specialized executor to set a property in a List or array.
+ * @since 2.0
+ */
+public final class ListSetExecutor extends AbstractExecutor.Set {
+        /** The java.lang.reflect.Array.get method used as an active marker in ListGet. */
+    private static final java.lang.reflect.Method ARRAY_SET =
+            initMarker(Array.class, "set", Object.class, Integer.TYPE, Object.class);
+    /** The java.util.obj.set method used as an active marker in ListSet. */
+    private static final java.lang.reflect.Method LIST_SET =
+            initMarker(List.class, "set", Integer.TYPE, Object.class);
+    /** The property. */
+    private final Integer property;
+
+    /**
+     * Creates an instance checking for the List interface or Array capability.
+     * @param is the introspector
+     * @param clazz the class that might implement the map interface
+     * @param key the key to use in obj.set(key,value)
+     * @param value the value to use in obj.set(key,value)
+     */
+    public ListSetExecutor(Introspector is, Class<?> clazz, Integer key, Object value) {
+        super(clazz, discover(clazz));
+        property = key;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object getTargetProperty() {
+        return property;
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    public Object execute(final Object obj, Object value) {
+        if (method == ARRAY_SET) {
+            java.lang.reflect.Array.set(obj, property.intValue(), value);
+        } else {
+            @SuppressWarnings("unchecked") // LSE should only be created for array or list types
+            final List<Object> list = (List<Object>) obj;
+            list.set(property.intValue(), value);
+        }
+        return value;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object tryExecute(final Object obj, Object key, Object value) {
+        if (obj != null && method != null
+            && objectClass.equals(obj.getClass())
+            && key instanceof Integer) {
+            if (method == ARRAY_SET) {
+                Array.set(obj, ((Integer) key).intValue(), value);
+            } else {
+                @SuppressWarnings("unchecked")  // LSE should only be created for array or list types
+                final List<Object> list = (List<Object>) obj;
+                list.set(((Integer) key).intValue(), value);
+            }
+            return value;
+        }
+        return TRY_FAILED;
+    }
+
+
+    /**
+     * Finds the method to perform 'set' on a obj of array.
+     * @param clazz the class to introspect
+     * @return a marker method, obj.set or array.set
+     */
+    static java.lang.reflect.Method discover(Class<?> clazz) {
+        if (clazz.isArray()) {
+            // we could verify if the call can be performed but it does not change
+            // the fact we would fail...
+            // Class<?> formal = clazz.getComponentType();
+            // Class<?> actual = value == null? Object.class : value.getClass();
+            // if (IntrospectionUtils.isMethodInvocationConvertible(formal, actual, false)) {
+                return ARRAY_SET;
+            // }
+        }
+        if (List.class.isAssignableFrom(clazz)) {
+            return LIST_SET;
+        }
+        return null;
+    }
+}
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/MapGetExecutor.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/MapGetExecutor.java
new file mode 100644
index 0000000..1633bbf
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/MapGetExecutor.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2.internal;
+
+import java.util.Map;
+
+ /**
+ * Specialized executor to get a property from a Map.
+  * @since 2.0
+ */
+public final class MapGetExecutor extends AbstractExecutor.Get {
+    /** The java.util.map.get method used as an active marker in MapGet. */
+    private static final java.lang.reflect.Method MAP_GET =
+            initMarker(Map.class, "get", Object.class);
+    /** The property. */
+    private final Object property;
+
+    /**
+     * Creates an instance checking for the Map interface.
+     * @param is the introspector
+     * @param clazz the class that might implement the map interface
+     * @param key the key to use in map.get(key)
+     */
+    public MapGetExecutor(Introspector is, Class<?> clazz, Object key) {
+        super(clazz, discover(clazz));
+        property = key;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object getTargetProperty() {
+        return property;
+    }
+    
+    /**
+     * Get the property from the map.
+     * @param obj the map.
+     * @return map.get(property)
+     */
+    @Override
+    public Object execute(final Object obj) {
+        @SuppressWarnings("unchecked") // ctor only allows Map instances - see discover() method
+        final Map<Object,?> map = (Map<Object, ?>) obj;
+        return map.get(property);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object tryExecute(final Object obj, Object key) {
+        if (obj != null &&  method != null
+            && objectClass.equals(obj.getClass())
+            && (key == null || property.getClass().equals(key.getClass()))) {
+            @SuppressWarnings("unchecked") // ctor only allows Map instances - see discover() method
+            final Map<Object,?> map = (Map<Object, ?>) obj;
+            return map.get(key);
+        }
+        return TRY_FAILED;
+    }
+
+    /**
+     * Finds the method to perform 'get' on a map.
+     * @param clazz the class to introspect
+     * @return a marker method, map.get
+     */
+    static java.lang.reflect.Method discover(Class<?> clazz) {
+        return (Map.class.isAssignableFrom(clazz))? MAP_GET : null;
+    }
+}
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/MapSetExecutor.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/MapSetExecutor.java
new file mode 100644
index 0000000..7fc001b
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/MapSetExecutor.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2.internal;
+import java.util.Map;
+import java.lang.reflect.InvocationTargetException;
+/**
+ * Specialized executor to set a property in a Map.
+ * @since 2.0
+ */
+public final class MapSetExecutor extends AbstractExecutor.Set {
+    /** The java.util.map.put method used as an active marker in MapSet. */
+    private static final java.lang.reflect.Method MAP_SET = initMarker(Map.class, "put", Object.class, Object.class);
+    /** The property. */
+    private final Object property;
+
+    /**
+     * Creates an instance checking for the Map interface.
+     *@param is the introspector
+     *@param clazz the class that might implement the map interface
+     *@param key the key to use as argument in map.put(key,value)
+     *@param value the value to use as argument in map.put(key,value)
+    */
+    public MapSetExecutor(Introspector is, Class<?> clazz, Object key, Object value) {
+        super(clazz, discover(clazz));
+        property = key;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object getTargetProperty() {
+        return property;
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    public Object execute(final Object obj, Object value)
+    throws IllegalAccessException, InvocationTargetException {
+        @SuppressWarnings("unchecked") // ctor only allows Map instances - see discover() method
+        final Map<Object,Object> map = ((Map<Object, Object>) obj);
+        map.put(property, value);
+        return value;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object tryExecute(final Object obj, Object key, Object value) {
+        if (obj != null && method != null
+            && objectClass.equals(obj.getClass())
+            && (key == null || property.getClass().equals(key.getClass()))) {
+            @SuppressWarnings("unchecked") // ctor only allows Map instances - see discover() method
+            final Map<Object,Object> map = ((Map<Object, Object>) obj);
+            map.put(key, value);
+            return value;
+        }
+        return TRY_FAILED;
+    }
+
+    /**
+     * Finds the method to perform 'set' on a map.
+     * @param clazz the class to introspect
+     * @return a marker method, map.get
+     */
+    static java.lang.reflect.Method discover(Class<?> clazz) {
+        return (Map.class.isAssignableFrom(clazz))? MAP_SET : null;
+    }
+}
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/MethodExecutor.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/MethodExecutor.java
new file mode 100644
index 0000000..fb36d00
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/MethodExecutor.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2.internal;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import org.apache.commons.jexl2.internal.introspection.MethodKey;
+
+/**
+ * Specialized executor to invoke a method on an object.
+ * @since 2.0
+ */
+public final class MethodExecutor extends AbstractExecutor.Method {
+    /** Whether this method handles varargs. */
+    private final boolean isVarArgs;
+    /**
+     * Creates a new instance.
+     * @param is the introspector used to discover the method
+     * @param obj the object to find the method in
+     * @param name the method name
+     * @param args the method arguments
+     */
+    public MethodExecutor(Introspector is, Object obj, String name, Object[] args) {
+        super(obj.getClass(), discover(is, obj, name, args));
+        isVarArgs = method != null && isVarArgMethod(method);
+    }
+
+    /**
+     * Invokes the method to be executed.
+     * @param o the object to invoke the method upon
+     * @param args the method arguments
+     * @return the result of the method invocation
+     * @throws IllegalAccessException Method is inaccessible.
+     * @throws InvocationTargetException Method body throws an exception.
+     */
+    @Override
+    public Object execute(Object o, Object[] args)
+        throws IllegalAccessException, InvocationTargetException  {
+        if (isVarArgs) {
+            Class<?>[] formal = method.getParameterTypes();
+            int index = formal.length - 1;
+            Class<?> type = formal[index].getComponentType();
+            if (args.length >= index) {
+                args = handleVarArg(type, index, args);
+            }
+        }
+        if (method.getDeclaringClass() == ArrayListWrapper.class && o.getClass().isArray()) {
+            return method.invoke(new ArrayListWrapper(o), args);
+        } else {
+            return method.invoke(o, args);
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object tryExecute(String name, Object o, Object[] args) {
+        MethodKey tkey = new MethodKey(name, args);
+        // let's assume that invocation will fly if the declaring class is the
+        // same and arguments have the same type
+        if (objectClass.equals(o.getClass()) && tkey.equals(key)) {
+            try {
+                return execute(o, args);
+            } catch (InvocationTargetException xinvoke) {
+                return TRY_FAILED; // fail
+            } catch (IllegalAccessException xill) {
+                return TRY_FAILED;// fail
+            }
+        }
+        return TRY_FAILED;
+    }
+
+
+    /**
+     * Discovers a method for a {@link MethodExecutor}.
+     * <p>
+     * If the object is an array, an attempt will be made to find the
+     * method in a List (see {@link ArrayListWrapper})
+     * </p>
+     * <p>
+     * If the object is a class, an attempt will be made to find the
+     * method as a static method of that class.
+     * </p>
+     * @param is the introspector used to discover the method
+     * @param obj the object to introspect
+     * @param method the name of the method to find
+     * @param args the method arguments
+     * @return a filled up parameter (may contain a null method)
+     */
+    private static Parameter discover(Introspector is,
+            Object obj, String method, Object[] args) {
+        final Class<?> clazz = obj.getClass();
+        final MethodKey key = new MethodKey(method, args);
+        java.lang.reflect.Method m = is.getMethod(clazz, key);
+        if (m == null && clazz.isArray()) {
+            // check for support via our array->list wrapper
+            m = is.getMethod(ArrayListWrapper.class, key);
+        }
+        if (m == null && obj instanceof Class<?>) {
+            m = is.getMethod((Class<?>) obj, key);
+        }
+        return new Parameter(m, key);
+    }
+
+    /**
+     * Reassembles arguments if the method is a vararg method.
+     * @param type   The vararg class type (aka component type
+     *               of the expected array arg)
+     * @param index  The index of the vararg in the method declaration
+     *               (This will always be one less than the number of
+     *               expected arguments.)
+     * @param actual The actual parameters being passed to this method
+     * @return The actual parameters adjusted for the varargs in order
+     * to fit the method declaration.
+     */
+    protected Object[] handleVarArg(Class<?> type, int index, Object[] actual) {
+        // if no values are being passed into the vararg
+        if (actual.length == index) {
+            // create an empty array of the expected type
+            actual = new Object[]{Array.newInstance(type, 0)};
+        } else if (actual.length == index + 1) {
+            // if one value is being passed into the vararg
+            // make sure the last arg is an array of the expected type
+            if (MethodKey.isInvocationConvertible(type,
+                    actual[index].getClass(),
+                    false)) {
+                // create a 1-length array to hold and replace the last param
+                Object lastActual = Array.newInstance(type, 1);
+                Array.set(lastActual, 0, actual[index]);
+                actual[index] = lastActual;
+            }
+        } else if (actual.length > index + 1) {
+            // if multiple values are being passed into the vararg
+            // put the last and extra actual in an array of the expected type
+            int size = actual.length - index;
+            Object lastActual = Array.newInstance(type, size);
+            for (int i = 0; i < size; i++) {
+                Array.set(lastActual, i, actual[index + i]);
+            }
+
+            // put all into a new actual array of the appropriate size
+            Object[] newActual = new Object[index + 1];
+            for (int i = 0; i < index; i++) {
+                newActual[i] = actual[i];
+            }
+            newActual[index] = lastActual;
+
+            // replace the old actual array
+            actual = newActual;
+        }
+        return actual;
+    }
+
+   /**
+     * Determines if a method can accept a variable number of arguments.
+     * @param m a the method to check
+     * @return true if method is vararg, false otherwise
+     */
+    private static boolean isVarArgMethod(java.lang.reflect.Method m) {
+        Class<?>[] formal = m.getParameterTypes();
+        if (formal == null || formal.length == 0) {
+            return false;
+        } else {
+            Class<?> last = formal[formal.length - 1];
+            // if the last arg is an array, then
+            // we consider this a varargs method
+            return last.isArray();
+        }
+    }
+}
+
+
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/PropertyGetExecutor.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/PropertyGetExecutor.java
new file mode 100644
index 0000000..fcf7b96
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/PropertyGetExecutor.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2.internal;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Specialized executor to get a property from an object.
+ * @since 2.0
+ */
+public final class PropertyGetExecutor extends AbstractExecutor.Get {
+    /** A static signature for method(). */
+    private static final Object[] EMPTY_PARAMS = {};
+    /** The property. */
+    private final String property;
+    
+    /**
+     * Creates an instance by attempting discovery of the get method.
+     * @param is the introspector
+     * @param clazz the class to introspect
+     * @param identifier the property to get
+     */
+    public PropertyGetExecutor(Introspector is, Class<?> clazz, String identifier) {
+        super(clazz, discover(is, clazz, identifier));
+        property = identifier;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object getTargetProperty() {
+        return property;
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    public Object execute(Object o)
+        throws IllegalAccessException, InvocationTargetException {
+        return method == null ? null : method.invoke(o, (Object[]) null);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object tryExecute(Object o, Object identifier) {
+        if (o != null && method !=  null
+            && property.equals(identifier)
+            && objectClass.equals(o.getClass())) {
+            try {
+                return method.invoke(o, (Object[]) null);
+            } catch (InvocationTargetException xinvoke) {
+                return TRY_FAILED; // fail
+            } catch (IllegalAccessException xill) {
+                return TRY_FAILED;// fail
+            }
+        }
+        return TRY_FAILED;
+    }
+
+    /**
+     * Discovers the method for a {@link PropertyGet}.
+     * <p>The method to be found should be named "get{P,p}property.</p>
+     *@param is the introspector
+     *@param clazz the class to find the get method from
+     *@param property the property name to find
+     *@return the method if found, null otherwise
+     */
+    static java.lang.reflect.Method discover(Introspector is,
+            final Class<?> clazz, String property) {
+        return discoverGet(is, "get", clazz, property);
+    }
+
+
+    /**
+     * Base method for boolean & object property get.
+     * @param is the introspector
+     * @param which "is" or "get" for boolean or object
+     * @param clazz The class being examined.
+     * @param property The property being addressed.
+     * @return The {get,is}{p,P}roperty method if one exists, null otherwise.
+     */
+    static java.lang.reflect.Method discoverGet(Introspector is,
+            String which, Class<?> clazz, String property) {
+        //  this is gross and linear, but it keeps it straightforward.
+        java.lang.reflect.Method method = null;
+        final int start = which.length(); // "get" or "is" so 3 or 2 for char case switch
+        // start with get<Property>
+        StringBuilder sb = new StringBuilder(which);
+        sb.append(property);
+        // uppercase nth char
+        char c = sb.charAt(start);
+        sb.setCharAt(start, Character.toUpperCase(c));
+        method = is.getMethod(clazz, sb.toString(), EMPTY_PARAMS);
+        //lowercase nth char
+        if (method == null) {
+            sb.setCharAt(start, Character.toLowerCase(c));
+            method = is.getMethod(clazz, sb.toString(), EMPTY_PARAMS);
+        }
+        return method;
+    }
+}
+
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/PropertySetExecutor.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/PropertySetExecutor.java
new file mode 100644
index 0000000..6812edf
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/PropertySetExecutor.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2.internal;
+import java.lang.reflect.InvocationTargetException;
+/**
+ * Specialized executor to set a property in an object.
+ * @since 2.0
+ */
+public final class PropertySetExecutor extends AbstractExecutor.Set {
+    /** Index of the first character of the set{p,P}roperty. */
+    private static final int SET_START_INDEX = 3;
+    /** The property. */
+    private final String property;
+
+    /**
+     * Creates an instance by attempting discovery of the set method.
+     * @param is the introspector
+     * @param clazz the class to introspect
+     * @param identifier the property to set
+     * @param arg the value to set into the property
+     */
+    public PropertySetExecutor(Introspector is, Class<?> clazz, String identifier, Object arg) {
+        super(clazz, discover(is, clazz, identifier, arg));
+        property = identifier;
+
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object getTargetProperty() {
+        return property;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object execute(Object o, Object arg)
+            throws IllegalAccessException, InvocationTargetException {
+        Object[] pargs = {arg};
+        if (method != null) {
+            method.invoke(o, pargs);
+        }
+        return arg;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Object tryExecute(Object o, Object identifier, Object arg) {
+        if (o != null && method != null
+            // ensure method name matches the property name
+            && property.equals(identifier)
+            // object class should be same as executor's method declaring class
+            && objectClass.equals(o.getClass())
+            // we are guaranteed the method has one parameter since it is a set(x)
+            && (arg == null || method.getParameterTypes()[0].equals(arg.getClass()))) {
+            try {
+                return execute(o, arg);
+            } catch (InvocationTargetException xinvoke) {
+                return TRY_FAILED; // fail
+            } catch (IllegalAccessException xill) {
+                return TRY_FAILED;// fail
+            }
+        }
+        return TRY_FAILED;
+    }
+
+
+    /**
+     * Discovers the method for a {@link PropertySet}.
+     * <p>The method to be found should be named "set{P,p}property.</p>
+     *@param is the introspector
+     *@param clazz the class to find the get method from
+     *@param property the name of the property to set
+     *@param arg the value to assign to the property
+     *@return the method if found, null otherwise
+     */
+    private static java.lang.reflect.Method discover(Introspector is,
+            final Class<?> clazz, String property, Object arg) {
+        // first, we introspect for the set<identifier> setter method
+        Object[] params = {arg};
+        StringBuilder sb = new StringBuilder("set");
+        sb.append(property);
+        // uppercase nth char
+        char c = sb.charAt(SET_START_INDEX);
+        sb.setCharAt(SET_START_INDEX, Character.toUpperCase(c));
+        java.lang.reflect.Method method = is.getMethod(clazz, sb.toString(), params);
+        // lowercase nth char
+        if (method == null) {
+            sb.setCharAt(SET_START_INDEX, Character.toLowerCase(c));
+            method = is.getMethod(clazz, sb.toString(), params);
+        }
+
+        return method;
+    }
+}
+
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/introspection/ClassMap.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/introspection/ClassMap.java
new file mode 100644
index 0000000..0ba01e5
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/introspection/ClassMap.java
@@ -0,0 +1,347 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2.internal.introspection;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+
+/**
+ * A cache of introspection information for a specific class instance.
+ * Keys objects by an agregation of the method name and the classes
+ * that make up the parameters.
+ * <p>
+ * Originally taken from the Velocity tree so we can be self-sufficient.
+ * </p>
+ * @see MethodKey
+ *
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @author <a href="mailto:bob@werken.com">Bob McWhirter</a>
+ * @author <a href="mailto:szegedia@freemail.hu">Attila Szegedi</a>
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
+ * @version $Id$
+ * @since 1.0
+ */
+final class ClassMap {
+    /** cache of methods. */
+    private final MethodCache methodCache;
+    /** cache of fields. */
+    private final Map<String, Field> fieldCache;
+
+    /**
+     * Standard constructor.
+     *
+     * @param aClass the class to deconstruct.
+     * @param log the logger.
+     */
+    ClassMap(Class<?> aClass, Log log) {
+        // eagerly cache methods
+        methodCache = createMethodCache(aClass, log);
+        // eagerly cache public fields
+        fieldCache = createFieldCache(aClass);
+    }
+
+    /**
+     * Find a Field using its name.
+     * <p>The clazz parameter <strong>must</strong> be this ClassMap key.</p>
+     * @param clazz the class to introspect
+     * @param fname the field name
+     * @return A Field object representing the field to invoke or null.
+     */
+    Field findField(final Class<?> clazz, final String fname) {
+        return fieldCache.get(fname);
+    }
+
+    /**
+     * Gets the field names cached by this map.
+     * @return the array of field names
+     */
+    String[] getFieldNames() {
+        return fieldCache.keySet().toArray(new String[fieldCache.size()]);
+    }
+
+    /**
+     * Creates a map of all public fields of a given class.
+     * @param clazz the class to introspect
+     * @return the map of fields (may be the empty map, can not be null)
+     */
+    private static Map<String,Field> createFieldCache(Class<?> clazz) {
+        Field[] fields = clazz.getFields();
+        if (fields.length > 0) {
+            Map<String, Field> cache = new HashMap<String, Field>();
+            for(Field field : fields) {
+                cache.put(field.getName(), field);
+            }
+            return cache;
+        } else {
+            return Collections.emptyMap();
+        }
+    }
+
+
+    /**
+     * Gets the methods names cached by this map.
+     * @return the array of method names
+     */
+    String[] getMethodNames() {
+        return methodCache.names();
+    }
+
+    /**
+     * Find a Method using the method name and parameter objects.
+     *
+     * @param key the method key
+     * @return A Method object representing the method to invoke or null.
+     * @throws MethodKey.AmbiguousException When more than one method is a match for the parameters.
+     */
+    Method findMethod(final MethodKey key)
+            throws MethodKey.AmbiguousException {
+        return methodCache.get(key);
+    }
+
+    /**
+     * Populate the Map of direct hits. These are taken from all the public methods
+     * that our class, its parents and their implemented interfaces provide.
+     * @param classToReflect the class to cache
+     * @param log the Log
+     * @return a newly allocated & filled up cache
+     */
+    private static MethodCache createMethodCache(Class<?> classToReflect, Log log) {
+        //
+        // Build a list of all elements in the class hierarchy. This one is bottom-first (i.e. we start
+        // with the actual declaring class and its interfaces and then move up (superclass etc.) until we
+        // hit java.lang.Object. That is important because it will give us the methods of the declaring class
+        // which might in turn be abstract further up the tree.
+        //
+        // We also ignore all SecurityExceptions that might happen due to SecurityManager restrictions (prominently
+        // hit with Tomcat 5.5).
+        //
+        // We can also omit all that complicated getPublic, getAccessible and upcast logic that the class map had up
+        // until Velocity 1.4. As we always reflect all elements of the tree (that's what we have a cache for), we will
+        // hit the public elements sooner or later because we reflect all the public elements anyway.
+        //
+        // Ah, the miracles of Java for(;;) ...
+        MethodCache cache = new MethodCache();
+        for (;classToReflect != null; classToReflect = classToReflect.getSuperclass()) {
+            if (Modifier.isPublic(classToReflect.getModifiers())) {
+                populateMethodCacheWith(cache, classToReflect, log);
+            }
+            Class<?>[] interfaces = classToReflect.getInterfaces();
+            for (int i = 0; i < interfaces.length; i++) {
+                populateMethodCacheWithInterface(cache, interfaces[i], log);
+            }
+        }
+        return cache;
+    }
+
+    /**
+     * Recurses up interface hierarchy to get all super interfaces.
+     * @param cache the cache to fill
+     * @param iface the interface to populate the cache from
+     * @param log the Log
+     */
+    private static void populateMethodCacheWithInterface(MethodCache cache, Class<?> iface, Log log) {
+        if (Modifier.isPublic(iface.getModifiers())) {
+            populateMethodCacheWith(cache, iface, log);
+        }
+        Class<?>[] supers = iface.getInterfaces();
+        for (int i = 0; i < supers.length; i++) {
+            populateMethodCacheWithInterface(cache, supers[i], log);
+        }
+    }
+
+    /**
+     * Recurses up class hierarchy to get all super classes.
+     * @param cache the cache to fill
+     * @param clazz the class to populate the cache from
+     * @param log the Log
+     */
+    private static void populateMethodCacheWith(MethodCache cache, Class<?> clazz, Log log) {
+        try {
+            Method[] methods = clazz.getDeclaredMethods();
+            for (int i = 0; i < methods.length; i++) {
+                int modifiers = methods[i].getModifiers();
+                if (Modifier.isPublic(modifiers)) {
+                    cache.put(methods[i]);
+                }
+            }
+        } catch (SecurityException se) {
+            // Everybody feels better with...
+            if (log.isDebugEnabled()) {
+                log.debug("While accessing methods of " + clazz + ": ", se);
+            }
+        }
+    }
+
+    /**
+     * This is the cache to store and look up the method information.
+     *
+     * @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
+     * @version $Id$
+     * <p>
+     * It stores the association between:
+     *  - a key made of a method name & an array of argument types.
+     *  - a method.
+     * </p>
+     * <p>
+     * Since the invocation of the associated method is dynamic, there is no need (nor way) to differentiate between
+     * foo(int,int) & foo(Integer,Integer) since in practise, only the latter form will be used through a call.
+     * This of course, applies to all 8 primitive types.
+     * </p>
+     * @version $Id$
+     */
+    static final class MethodCache {
+        /**
+         * A method that returns itself used as a marker for cache miss,
+         * allows the underlying cache map to be strongly typed.
+         * @return itself as a method
+         */
+        public static Method cacheMiss() {
+            try {
+                return MethodCache.class.getMethod("cacheMiss");
+            } catch (Exception xio) {
+                // this really cant make an error...
+                return null;
+            }
+        }
+        /** The cache miss marker method. */
+        private static final Method CACHE_MISS = cacheMiss();
+        /** The initial size of the primitive conversion map. */
+        private static final int PRIMITIVE_SIZE = 13;
+        /** The primitive type to class conversion map. */
+        private static final Map<Class<?>, Class<?>> PRIMITIVE_TYPES;
+        static {
+            PRIMITIVE_TYPES = new HashMap<Class<?>, Class<?>>(PRIMITIVE_SIZE);
+            PRIMITIVE_TYPES.put(Boolean.TYPE, Boolean.class);
+            PRIMITIVE_TYPES.put(Byte.TYPE, Byte.class);
+            PRIMITIVE_TYPES.put(Character.TYPE, Character.class);
+            PRIMITIVE_TYPES.put(Double.TYPE, Double.class);
+            PRIMITIVE_TYPES.put(Float.TYPE, Float.class);
+            PRIMITIVE_TYPES.put(Integer.TYPE, Integer.class);
+            PRIMITIVE_TYPES.put(Long.TYPE, Long.class);
+            PRIMITIVE_TYPES.put(Short.TYPE, Short.class);
+        }
+
+        /** Converts a primitive type to its corresponding class.
+         * <p>
+         * If the argument type is primitive then we want to convert our
+         * primitive type signature to the corresponding Object type so
+         * introspection for methods with primitive types will work
+         * correctly.
+         * </p>
+         * @param parm a may-be primitive type class
+         * @return the equivalent object class 
+         */
+        static Class<?> primitiveClass(Class<?> parm) {
+            // it is marginally faster to get from the map than call isPrimitive...
+            //if (!parm.isPrimitive()) return parm;
+            Class<?> prim = PRIMITIVE_TYPES.get(parm);
+            return prim == null ? parm : prim;
+        }
+        /**
+         * The method cache.
+         * <p>
+         * Cache of Methods, or CACHE_MISS, keyed by method
+         * name and actual arguments used to find it.
+         * </p>
+         */
+        private final Map<MethodKey, Method> methods = new HashMap<MethodKey, Method>();
+        /**
+         * Map of methods that are searchable according to method parameters to find a match.
+         */
+        private final MethodMap methodMap = new MethodMap();
+
+        /**
+         * Find a Method using the method name and parameter objects.
+         *<p>
+         * Look in the methodMap for an entry.  If found,
+         * it'll either be a CACHE_MISS, in which case we
+         * simply give up, or it'll be a Method, in which
+         * case, we return it.
+         *</p>
+         * <p>
+         * If nothing is found, then we must actually go
+         * and introspect the method from the MethodMap.
+         *</p>
+         * @param methodKey the method key
+         * @return A Method object representing the method to invoke or null.
+         * @throws MethodKey.AmbiguousException When more than one method is a match for the parameters.
+         */
+        Method get(final MethodKey methodKey) throws MethodKey.AmbiguousException {
+            synchronized (methodMap) {
+                Method cacheEntry = methods.get(methodKey);
+                // We looked this up before and failed.
+                if (cacheEntry == CACHE_MISS) {
+                    return null;
+                }
+
+                if (cacheEntry == null) {
+                    try {
+                        // That one is expensive...
+                        cacheEntry = methodMap.find(methodKey);
+                        if (cacheEntry != null) {
+                            methods.put(methodKey, cacheEntry);
+                        } else {
+                            methods.put(methodKey, CACHE_MISS);
+                        }
+                    } catch (MethodKey.AmbiguousException ae) {
+                        // that's a miss :-)
+                        methods.put(methodKey, CACHE_MISS);
+                        throw ae;
+                    }
+                }
+
+                // Yes, this might just be null.
+                return cacheEntry;
+            }
+        }
+
+        /**
+         * Adds a method to the map.
+         * @param method the method to add
+         */
+        void put(Method method) {
+            synchronized (methodMap) {
+                MethodKey methodKey = new MethodKey(method);
+                // We don't overwrite methods. Especially not if we fill the
+                // cache from defined class towards java.lang.Object because
+                // abstract methods in superclasses would else overwrite concrete
+                // classes further down the hierarchy.
+                if (methods.get(methodKey) == null) {
+                    methods.put(methodKey, method);
+                    methodMap.add(method);
+                }
+            }
+        }
+
+        /**
+         * Gets all the method names from this map.
+         * @return the array of method name
+         */
+        String[] names() {
+            synchronized (methodMap) {
+                return methodMap.names();
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/introspection/IntrospectorBase.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/introspection/IntrospectorBase.java
new file mode 100644
index 0000000..2888ce9
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/introspection/IntrospectorBase.java
@@ -0,0 +1,275 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2.internal.introspection;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.LinkedList;
+
+import org.apache.commons.logging.Log;
+
+/**
+ * This basic function of this class is to return a Method object for a
+ * particular class given the name of a method and the parameters to the method
+ * in the form of an Object[]
+ * <p/>
+ * The first time the Introspector sees a class it creates a class method map
+ * for the class in question. Basically the class method map is a Hastable where
+ * Method objects are keyed by a concatenation of the method name and the names
+ * of classes that make up the parameters.
+ *
+ * For example, a method with the following signature:
+ *
+ * public void method(String a, StringBuffer b)
+ *
+ * would be mapped by the key:
+ *
+ * "method" + "java.lang.String" + "java.lang.StringBuffer"
+ *
+ * This mapping is performed for all the methods in a class and stored for
+ *
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @author <a href="mailto:bob@werken.com">Bob McWhirter</a>
+ * @author <a href="mailto:szegedia@freemail.hu">Attila Szegedi</a>
+ * @author <a href="mailto:paulo.gaspar@krankikom.de">Paulo Gaspar</a>
+ * @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
+ * @version $Id$
+ * @since 1.0
+ */
+public class IntrospectorBase {
+    /** the logger. */
+    protected final Log rlog;
+    /**
+     * Holds the method maps for the classes we know about, keyed by Class.
+     */
+    private final Map<Class<?>, ClassMap> classMethodMaps = new HashMap<Class<?>, ClassMap>();
+    /**
+     * The class loader used to solve constructors if needed.
+     */
+    private ClassLoader loader;
+    /**
+     * Holds the map of classes ctors we know about as well as unknown ones.
+     */
+    private final Map<MethodKey, Constructor<?>> constructorsMap = new HashMap<MethodKey, Constructor<?>>();
+    /**
+     * Holds the set of classes we have introspected.
+     */
+    private final Map<String, Class<?>> constructibleClasses = new HashMap<String, Class<?>>();
+
+    /**
+     * Create the introspector.
+     * @param log the logger to use
+     */
+    public IntrospectorBase(Log log) {
+        this.rlog = log;
+        loader = getClass().getClassLoader();
+    }
+
+    /**
+     * Gets the method defined by the <code>MethodKey</code> for the class <code>c</code>.
+     *
+     * @param c     Class in which the method search is taking place
+     * @param key   Key of the method being searched for
+     * @return The desired Method object.
+     * @throws IllegalArgumentException     When the parameters passed in can not be used for introspection.
+     *  
+     */
+    //CSOFF: RedundantThrows
+    public Method getMethod(Class<?> c, MethodKey key) {
+        try {
+            ClassMap classMap = getMap(c);
+            return classMap.findMethod(key);
+        } catch (MethodKey.AmbiguousException ae) {
+            // whoops.  Ambiguous.  Make a nice log message and return null...
+            if (rlog != null) {
+                rlog.error("ambiguous method invocation: "
+                           + c.getName() + "."
+                           + key.debugString());
+            }
+        }
+        return null;
+
+    }
+    // CSON: RedundantThrows
+
+
+    /**
+     * Gets the field named by <code>key</code> for the class <code>c</code>.
+     *
+     * @param c     Class in which the field search is taking place
+     * @param key   Name of the field being searched for
+     * @return the desired field or null if it does not exist or is not accessible
+     * */
+    public Field getField(Class<?> c, String key) {
+        ClassMap classMap = getMap(c);
+        return classMap.findField(c, key);
+    }
+
+    /**
+     * Gets the array of accessible field names known for a given class.
+     * @param c the class
+     * @return the class field names
+     */
+    public String[] getFieldNames(Class<?> c) {
+        if (c == null) {
+            return new String[0];
+        }
+        ClassMap classMap = getMap(c);
+        return classMap.getFieldNames();
+    }
+
+    /**
+     * Gets the array of accessible methods names known for a given class.
+     * @param c the class
+     * @return the class method names
+     */
+    public String[] getMethodNames(Class<?> c) {
+        if (c == null) {
+            return new String[0];
+        }
+        ClassMap classMap = getMap(c);
+        return classMap.getMethodNames();
+    }
+
+    /**
+     * A Constructor get cache-miss.
+     */
+    private static class CacheMiss {
+        /** The constructor used as cache-miss. */
+        @SuppressWarnings("unused")
+        public CacheMiss() {}
+    }
+    /** The cache-miss marker for the constructors map. */
+    private static final Constructor<?> CTOR_MISS = CacheMiss.class.getConstructors()[0];
+    
+    /**
+     * Sets the class loader used to solve constructors.
+     * <p>Also cleans the constructors cache.</p>
+     * @param cloader the class loader; if null, use this instance class loader
+     */
+    public void setLoader(ClassLoader cloader) {
+        if (cloader == null) {
+            cloader = getClass().getClassLoader();
+        }
+        if (!cloader.equals(loader)) {
+            synchronized(constructorsMap) {
+                loader = cloader;
+                constructorsMap.clear();
+                constructibleClasses.clear();
+            }
+        }
+    }
+
+    /**
+     * Gets the constructor defined by the <code>MethodKey</code>.
+     *
+     * @param key   Key of the constructor being searched for
+     * @return The desired Constructor object.
+     * @throws IllegalArgumentException     When the parameters passed in can not be used for introspection.
+     */
+    public Constructor<?> getConstructor(final MethodKey key) {
+        return getConstructor(null, key);
+    }
+    
+    /**
+     * Gets the constructor defined by the <code>MethodKey</code>.
+     * @param c the class we want to instantiate
+     * @param key   Key of the constructor being searched for
+     * @return The desired Constructor object.
+     * @throws IllegalArgumentException     When the parameters passed in can not be used for introspection.
+     */
+    //CSOFF: RedundantThrows
+    public Constructor<?> getConstructor(final Class<?> c, final MethodKey key) {
+        try {
+            Constructor<?> ctor = null;
+            synchronized(constructorsMap) {
+                ctor = constructorsMap.get(key);
+                // that's a clear miss
+                if (CTOR_MISS.equals(ctor)) {
+                    return null;
+                }
+                // let's introspect...
+                if (ctor == null) {
+                    final String cname = key.getMethod();
+                    // do we know about this class?
+                    Class<?> clazz = constructibleClasses.get(cname);
+                    try {
+                        // do find the most specific ctor
+                        if (clazz == null) {
+                            if (c != null && c.getName().equals(key.getMethod())) {
+                                clazz = c;
+                            } else {
+                                clazz = loader.loadClass(cname);
+                            }
+                            // add it to list of known loaded classes
+                            constructibleClasses.put(cname, clazz);
+                        }
+                        List<Constructor<?>> l = new LinkedList<Constructor<?>>();
+                        for(Constructor<?> ictor : clazz.getConstructors()) {
+                            l.add(ictor);
+                        }
+                        // try to find one
+                        ctor = key.getMostSpecificConstructor(l);
+                        if (ctor != null) {
+                            constructorsMap.put(key, ctor);
+                        } else {
+                            constructorsMap.put(key, CTOR_MISS);
+                        }
+                    } catch(ClassNotFoundException xnotfound) {
+                        if (rlog.isDebugEnabled()) {
+                            rlog.debug("could not load class " + cname, xnotfound);
+                        }
+                        ctor = null;
+                    } catch(MethodKey.AmbiguousException xambiguous) {
+                        rlog.warn("ambiguous ctor detected for " + cname, xambiguous);
+                        ctor = null;
+                    }
+                }
+            }
+            return ctor;
+        } catch (MethodKey.AmbiguousException ae) {
+            // whoops.  Ambiguous.  Make a nice log message and return null...
+            if (rlog != null) {
+                rlog.error("ambiguous constructor invocation: new "
+                           + key.debugString());
+            }
+        }
+        return null;
+    }
+    // CSON: RedundantThrows
+
+    /**
+     * Gets the ClassMap for a given class.
+     * @param c the class
+     * @return the class map
+     */
+    private ClassMap getMap(Class<?> c) {
+        synchronized (classMethodMaps) {
+            ClassMap classMap = classMethodMaps.get(c);
+            if (classMap == null) {
+                classMap = new ClassMap(c,rlog);
+                classMethodMaps.put(c, classMap);
+            }
+            return classMap;
+        }
+    }
+}
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/introspection/MethodKey.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/introspection/MethodKey.java
new file mode 100644
index 0000000..e2560c8
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/introspection/MethodKey.java
@@ -0,0 +1,648 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2.internal.introspection;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Iterator;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+/**
+ * A method key usable by the introspector cache.
+ * <p>
+ * This stores a method (or class) name and parameters.
+ * </p>
+ * <p>
+ * This replaces the original key scheme which used to build the key
+ * by concatenating the method name and parameters class names as one string
+ * with the exception that primitive types were converted to their object class equivalents.
+ * </p>
+ * <p>
+ * The key is still based on the same information, it is just wrapped in an object instead.
+ * Primitive type classes are converted to they object equivalent to make a key;
+ * int foo(int) and int foo(Integer) do generate the same key.
+ * </p>
+ * A key can be constructed either from arguments (array of objects) or from parameters
+ * (array of class).
+ * Roughly 3x faster than string key to access the map & uses less memory.
+ *
+ * For the parameters methods:
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @author <a href="mailto:bob@werken.com">Bob McWhirter</a>
+ * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph Reck</a>
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @author <a href="mailto:szegedia@freemail.hu">Attila Szegedi</a>
+ * @author Nathan Bubna
+ */
+public final class MethodKey {
+    /** The hash code. */
+    private final int hashCode;
+    /** The method name. */
+    private final String method;
+    /** The parameters. */
+    private final Class<?>[] params;
+    /** A marker for empty parameter list. */
+    private static final Class<?>[] NOARGS = new Class<?>[0];
+    /** The hash code constants. */
+    private static final int HASH = 37;
+
+    /**
+     * Creates a key from a method name and a set of arguments.
+     * @param aMethod the method to generate the key from
+     * @param args the intended method arguments
+     */
+    public MethodKey(String aMethod, Object[] args) {
+        super();
+        // !! keep this in sync with the other ctor (hash code) !!
+        this.method = aMethod;
+        int hash = this.method.hashCode();
+        final int size;
+        // CSOFF: InnerAssignment
+        if (args != null && (size = args.length) > 0) {
+            this.params = new Class<?>[size];
+            for (int p = 0; p < size; ++p) {
+                Object arg = args[p];
+                // null arguments use void as Void.class as marker
+                Class<?> parm = arg == null ? Void.class : arg.getClass();
+                hash = (HASH * hash) + parm.hashCode();
+                this.params[p] = parm;
+            }
+        } else {
+            this.params = NOARGS;
+        }
+        this.hashCode = hash;
+    }
+
+    /**
+     * Creates a key from a method.
+     * @param aMethod the method to generate the key from.
+     */
+    MethodKey(Method aMethod) {
+        this(aMethod.getName(), aMethod.getParameterTypes());
+    }
+
+    /**
+     * Creates a key from a method name and a set of parameters.
+     * @param aMethod the method to generate the key from
+     * @param args the intended method parameters
+     */
+    MethodKey(String aMethod, Class<?>[] args) {
+        super();
+        // !! keep this in sync with the other ctor (hash code) !!
+        this.method = aMethod.intern();
+        int hash = this.method.hashCode();
+        final int size;
+        // CSOFF: InnerAssignment
+        if (args != null && (size = args.length) > 0) {
+            this.params = new Class<?>[size];
+            for (int p = 0; p < size; ++p) {
+                Class<?> parm = ClassMap.MethodCache.primitiveClass(args[p]);
+                hash = (HASH * hash) + parm.hashCode();
+                this.params[p] = parm;
+            }
+        } else {
+            this.params = NOARGS;
+        }
+        this.hashCode = hash;
+    }
+
+    /**
+     * Gets this key's method name.
+     * @return the method name
+     */
+    String getMethod() {
+        return method;
+    }
+
+    /**
+     * Gets this key's method parameter classes.
+     * @return the parameters
+     */
+    Class<?>[] getParameters() {
+        return params;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public int hashCode() {
+        return hashCode;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof MethodKey) {
+            MethodKey key = (MethodKey) obj;
+            return method.equals(key.method) && Arrays.equals(params, key.params);
+        }
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder(method);
+        for (Class<?> c : params) {
+            builder.append(c == Void.class ? "null" : c.getName());
+        }
+        return builder.toString();
+    }
+
+    /**
+     * Outputs a human readable debug representation of this key.
+     * @return method(p0, p1, ...)
+     */
+    public String debugString() {
+        StringBuilder builder = new StringBuilder(method);
+        builder.append('(');
+        for (int i = 0; i < params.length; i++) {
+            if (i > 0) {
+                builder.append(", ");
+            }
+            builder.append(Void.class == params[i] ? "null" : params[i].getName());
+        }
+        builder.append(')');
+        return builder.toString();
+    }
+
+    /**
+     * Gets the most specific method that is applicable to the parameters of this key.
+     * @param methods a list of methods.
+     * @return the most specific method.
+     * @throws MethodKey.AmbiguousException if there is more than one.
+     */
+    public Method getMostSpecificMethod(List<Method> methods) {
+        return METHODS.getMostSpecific(methods, params);
+    }
+
+    /**
+     * Gets the most specific constructor that is applicable to the parameters of this key.
+     * @param methods a list of constructors.
+     * @return the most specific constructor.
+     * @throws MethodKey.AmbiguousException if there is more than one.
+     */
+    public Constructor<?> getMostSpecificConstructor(List<Constructor<?>> methods) {
+        return CONSTRUCTORS.getMostSpecific(methods, params);
+    }
+
+    /**
+     * Determines whether a type represented by a class object is
+     * convertible to another type represented by a class object using a
+     * method invocation conversion, treating object types of primitive
+     * types as if they were primitive types (that is, a Boolean actual
+     * parameter type matches boolean primitive formal type). This behavior
+     * is because this method is used to determine applicable methods for
+     * an actual parameter list, and primitive types are represented by
+     * their object duals in reflective method calls.
+     *
+     * @param formal         the formal parameter type to which the actual
+     *                       parameter type should be convertible
+     * @param actual         the actual parameter type.
+     * @param possibleVarArg whether or not we're dealing with the last parameter
+     *                       in the method declaration
+     * @return true if either formal type is assignable from actual type,
+     *         or formal is a primitive type and actual is its corresponding object
+     *         type or an object type of a primitive type that can be converted to
+     *         the formal type.
+     */
+    public static boolean isInvocationConvertible(Class<?> formal,
+                                                  Class<?> actual,
+                                                  boolean possibleVarArg) {
+        /* if it's a null, it means the arg was null */
+        if (actual == null && !formal.isPrimitive()) {
+            return true;
+        }
+
+        /* Check for identity or widening reference conversion */
+        if (actual != null && formal.isAssignableFrom(actual)) {
+            return true;
+        }
+
+        // CSOFF: NeedBraces
+        /* Check for boxing with widening primitive conversion. Note that
+         * actual parameters are never primitives. */
+        if (formal.isPrimitive()) {
+            if (formal == Boolean.TYPE && actual == Boolean.class)
+                return true;
+            if (formal == Character.TYPE && actual == Character.class)
+                return true;
+            if (formal == Byte.TYPE && actual == Byte.class)
+                return true;
+            if (formal == Short.TYPE
+                && (actual == Short.class || actual == Byte.class))
+                return true;
+            if (formal == Integer.TYPE
+                && (actual == Integer.class || actual == Short.class
+                    || actual == Byte.class))
+                return true;
+            if (formal == Long.TYPE
+                && (actual == Long.class || actual == Integer.class
+                    || actual == Short.class || actual == Byte.class))
+                return true;
+            if (formal == Float.TYPE
+                && (actual == Float.class || actual == Long.class
+                    || actual == Integer.class || actual == Short.class
+                    || actual == Byte.class))
+                return true;
+            if (formal == Double.TYPE
+                && (actual == Double.class || actual == Float.class
+                    || actual == Long.class || actual == Integer.class
+                    || actual == Short.class || actual == Byte.class))
+                return true;
+        }
+        // CSON: NeedBraces
+
+        /* Check for vararg conversion. */
+        if (possibleVarArg && formal.isArray()) {
+            if (actual != null && actual.isArray()) {
+                actual = actual.getComponentType();
+            }
+            return isInvocationConvertible(formal.getComponentType(),
+                    actual, false);
+        }
+        return false;
+    }
+
+    /**
+     * Determines whether a type represented by a class object is
+     * convertible to another type represented by a class object using a
+     * method invocation conversion, without matching object and primitive
+     * types. This method is used to determine the more specific type when
+     * comparing signatures of methods.
+     *
+     * @param formal         the formal parameter type to which the actual
+     *                       parameter type should be convertible
+     * @param actual         the actual parameter type.
+     * @param possibleVarArg whether or not we're dealing with the last parameter
+     *                       in the method declaration
+     * @return true if either formal type is assignable from actual type,
+     *         or formal and actual are both primitive types and actual can be
+     *         subject to widening conversion to formal.
+     */
+    public static boolean isStrictInvocationConvertible(Class<?> formal,
+                                                        Class<?> actual,
+                                                        boolean possibleVarArg) {
+        /* we shouldn't get a null into, but if so */
+        if (actual == null && !formal.isPrimitive()) {
+            return true;
+        }
+
+        /* Check for identity or widening reference conversion */
+        if (formal.isAssignableFrom(actual)) {
+            return true;
+        }
+
+        // CSOFF: NeedBraces
+        /* Check for widening primitive conversion. */
+        if (formal.isPrimitive()) {
+            if (formal == Short.TYPE && (actual == Byte.TYPE))
+                return true;
+            if (formal == Integer.TYPE
+                && (actual == Short.TYPE || actual == Byte.TYPE))
+                return true;
+            if (formal == Long.TYPE
+                && (actual == Integer.TYPE || actual == Short.TYPE
+                    || actual == Byte.TYPE))
+                return true;
+            if (formal == Float.TYPE
+                && (actual == Long.TYPE || actual == Integer.TYPE
+                    || actual == Short.TYPE || actual == Byte.TYPE))
+                return true;
+            if (formal == Double.TYPE
+                && (actual == Float.TYPE || actual == Long.TYPE
+                    || actual == Integer.TYPE || actual == Short.TYPE
+                    || actual == Byte.TYPE))
+                return true;
+        }
+        // CSON: NeedBraces
+
+        /* Check for vararg conversion. */
+        if (possibleVarArg && formal.isArray()) {
+            if (actual != null && actual.isArray()) {
+                actual = actual.getComponentType();
+            }
+            return isStrictInvocationConvertible(formal.getComponentType(),
+                    actual, false);
+        }
+        return false;
+    }
+
+    /**
+     * whether a method/ctor is more specific than a previously compared one.
+     */
+    private static final int MORE_SPECIFIC = 0;
+    /**
+     * whether a method/ctor is less specific than a previously compared one.
+     */
+    private static final int LESS_SPECIFIC = 1;
+    /**
+     * A method/ctor doesn't match a previously compared one.
+     */
+    private static final int INCOMPARABLE = 2;
+
+    /**
+     * Simple distinguishable exception, used when
+     * we run across ambiguous overloading.  Caught
+     * by the introspector.
+     */
+    public static class AmbiguousException extends RuntimeException {
+        /**
+         * Version Id for serializable.
+         */
+        private static final long serialVersionUID = -2314636505414551664L;
+    }
+
+    /**
+     * Utility for parameters matching.
+     * @param <T> Method or Constructor
+     */
+     private abstract static class Parameters<T> {
+        /**
+         * Extract the parameter types from its applicable argument.
+         * @param app a method or constructor
+         * @return the parameters
+         */
+        protected abstract Class<?>[] getParameterTypes(T app);
+
+        // CSOFF: RedundantThrows
+        /**
+         * Gets the most specific method that is applicable to actual argument types.
+         * @param methods a list of methods.
+         * @param classes list of argument types.
+         * @return the most specific method.
+         * @throws MethodKey.AmbiguousException if there is more than one.
+         */
+        private T getMostSpecific(List<T> methods, Class<?>[] classes) {
+            LinkedList<T> applicables = getApplicables(methods, classes);
+
+            if (applicables.isEmpty()) {
+                return null;
+            }
+
+            if (applicables.size() == 1) {
+                return applicables.getFirst();
+            }
+
+            /*
+             * This list will contain the maximally specific methods. Hopefully at
+             * the end of the below loop, the list will contain exactly one method,
+             * (the most specific method) otherwise we have ambiguity.
+             */
+
+            LinkedList<T> maximals = new LinkedList<T>();
+
+            for (Iterator<T> applicable = applicables.iterator();
+                    applicable.hasNext();) {
+                T app = applicable.next();
+                Class<?>[] appArgs = getParameterTypes(app);
+
+                boolean lessSpecific = false;
+
+                for (Iterator<T> maximal = maximals.iterator();
+                        !lessSpecific && maximal.hasNext();) {
+                    T max = maximal.next();
+
+                    // CSOFF: MissingSwitchDefault
+                    switch (moreSpecific(appArgs, getParameterTypes(max))) {
+                        case MORE_SPECIFIC:
+                            /*
+                             * This method is more specific than the previously
+                             * known maximally specific, so remove the old maximum.
+                             */
+                            maximal.remove();
+                            break;
+
+                        case LESS_SPECIFIC:
+                            /*
+                             * This method is less specific than some of the
+                             * currently known maximally specific methods, so we
+                             * won't add it into the set of maximally specific
+                             * methods
+                             */
+
+                            lessSpecific = true;
+                            break;
+                    }
+                } // CSON: MissingSwitchDefault
+
+                if (!lessSpecific) {
+                    maximals.addLast(app);
+                }
+            }
+
+            if (maximals.size() > 1) {
+                // We have more than one maximally specific method
+                throw new AmbiguousException();
+            }
+
+            return maximals.getFirst();
+        } // CSON: RedundantThrows
+
+        /**
+         * Determines which method signature (represented by a class array) is more
+         * specific. This defines a partial ordering on the method signatures.
+         *
+         * @param c1 first signature to compare
+         * @param c2 second signature to compare
+         * @return MORE_SPECIFIC if c1 is more specific than c2, LESS_SPECIFIC if
+         *         c1 is less specific than c2, INCOMPARABLE if they are incomparable.
+         */
+        private int moreSpecific(Class<?>[] c1, Class<?>[] c2) {
+            boolean c1MoreSpecific = false;
+            boolean c2MoreSpecific = false;
+
+            // compare lengths to handle comparisons where the size of the arrays
+            // doesn't match, but the methods are both applicable due to the fact
+            // that one is a varargs method
+            if (c1.length > c2.length) {
+                return MORE_SPECIFIC;
+            }
+            if (c2.length > c1.length) {
+                return LESS_SPECIFIC;
+            }
+
+            // ok, move on and compare those of equal lengths
+            for (int i = 0; i < c1.length; ++i) {
+                if (c1[i] != c2[i]) {
+                    boolean last = (i == c1.length - 1);
+                    c1MoreSpecific = c1MoreSpecific || isStrictConvertible(c2[i], c1[i], last);
+                    c2MoreSpecific = c2MoreSpecific || isStrictConvertible(c1[i], c2[i], last);
+                }
+            }
+
+            if (c1MoreSpecific) {
+                if (c2MoreSpecific) {
+                    /*
+                     *  Incomparable due to cross-assignable arguments (i.e.
+                     * foo(String, Object) vs. foo(Object, String))
+                     */
+
+                    return INCOMPARABLE;
+                }
+
+                return MORE_SPECIFIC;
+            }
+
+            if (c2MoreSpecific) {
+                return LESS_SPECIFIC;
+            }
+
+            /*
+             * Incomparable due to non-related arguments (i.e.
+             * foo(Runnable) vs. foo(Serializable))
+             */
+
+            return INCOMPARABLE;
+        }
+
+        /**
+         * Returns all methods that are applicable to actual argument types.
+         *
+         * @param methods list of all candidate methods
+         * @param classes the actual types of the arguments
+         * @return a list that contains only applicable methods (number of
+         *         formal and actual arguments matches, and argument types are assignable
+         *         to formal types through a method invocation conversion).
+         */
+        private LinkedList<T> getApplicables(List<T> methods, Class<?>[] classes) {
+            LinkedList<T> list = new LinkedList<T>();
+
+            for (Iterator<T> imethod = methods.iterator(); imethod.hasNext();) {
+                T method = imethod.next();
+                if (isApplicable(method, classes)) {
+                    list.add(method);
+                }
+
+            }
+            return list;
+        }
+
+        /**
+         * Returns true if the supplied method is applicable to actual
+         * argument types.
+         *
+         * @param method  method that will be called
+         * @param classes arguments to method
+         * @return true if method is applicable to arguments
+         */
+        private boolean isApplicable(T method, Class<?>[] classes) {
+            Class<?>[] methodArgs = getParameterTypes(method);
+
+            if (methodArgs.length > classes.length) {
+                // if there's just one more methodArg than class arg
+                // and the last methodArg is an array, then treat it as a vararg
+                return methodArgs.length == classes.length + 1 && methodArgs[methodArgs.length - 1].isArray();
+            }
+            if (methodArgs.length == classes.length) {
+                // this will properly match when the last methodArg
+                // is an array/varargs and the last class is the type of array
+                // (e.g. String when the method is expecting String...)
+                for (int i = 0; i < classes.length; ++i) {
+                    if (!isConvertible(methodArgs[i], classes[i], false)) {
+                        // if we're on the last arg and the method expects an array
+                        if (i == classes.length - 1 && methodArgs[i].isArray()) {
+                            // check to see if the last arg is convertible
+                            // to the array's component type
+                            return isConvertible(methodArgs[i], classes[i], true);
+                        }
+                        return false;
+                    }
+                }
+                return true;
+            }
+            // more arguments given than the method accepts; check for varargs
+            if (methodArgs.length > 0) {
+                // check that the last methodArg is an array
+                Class<?> lastarg = methodArgs[methodArgs.length - 1];
+                if (!lastarg.isArray()) {
+                    return false;
+                }
+
+                // check that they all match up to the last method arg
+                for (int i = 0; i < methodArgs.length - 1; ++i) {
+                    if (!isConvertible(methodArgs[i], classes[i], false)) {
+                        return false;
+                    }
+                }
+
+                // check that all remaining arguments are convertible to the vararg type
+                Class<?> vararg = lastarg.getComponentType();
+                for (int i = methodArgs.length - 1; i < classes.length; ++i) {
+                    if (!isConvertible(vararg, classes[i], false)) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+            // no match
+            return false;
+        }
+
+        /**
+         * @see #isInvocationConvertible(Class, Class, boolean)
+         * @param formal         the formal parameter type to which the actual
+         *                       parameter type should be convertible
+         * @param actual         the actual parameter type.
+         * @param possibleVarArg whether or not we're dealing with the last parameter
+         *                       in the method declaration
+         * @return see isMethodInvocationConvertible.
+         */
+        private boolean isConvertible(Class<?> formal, Class<?> actual,
+                boolean possibleVarArg) {
+            // if we see Void.class, the argument was null
+            return isInvocationConvertible(formal, actual.equals(Void.class)? null : actual, possibleVarArg);
+        }
+
+        /**
+         * @see #isStrictInvocationConvertible(Class, Class, boolean)
+         * @param formal         the formal parameter type to which the actual
+         *                       parameter type should be convertible
+         * @param actual         the actual parameter type.
+         * @param possibleVarArg whether or not we're dealing with the last parameter
+         *                       in the method declaration
+         * @return see isStrictMethodInvocationConvertible.
+         */
+        private boolean isStrictConvertible(Class<?> formal, Class<?> actual,
+                boolean possibleVarArg) {
+            // if we see Void.class, the argument was null
+            return isStrictInvocationConvertible(formal, actual.equals(Void.class)? null : actual, possibleVarArg);
+        }
+
+    }
+
+    /**
+     * The parameter matching service for methods.
+     */
+    private static final Parameters<Method> METHODS = new Parameters<Method>() {
+        @Override
+        protected Class<?>[] getParameterTypes(Method app) {
+            return app.getParameterTypes();
+        }
+    };
+
+
+    /**
+     * The parameter matching service for constructors.
+     */
+    private static final Parameters<Constructor<?>> CONSTRUCTORS = new Parameters<Constructor<?>>() {
+        @Override
+        protected Class<?>[] getParameterTypes(Constructor<?> app) {
+            return app.getParameterTypes();
+        }
+    };
+}
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/introspection/MethodMap.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/introspection/MethodMap.java
new file mode 100644
index 0000000..dfc429e
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/introspection/MethodMap.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2.internal.introspection;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
+ * @author <a href="mailto:bob@werken.com">Bob McWhirter</a>
+ * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph Reck</a>
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @author <a href="mailto:szegedia@freemail.hu">Attila Szegedi</a>
+ * @version $Id$
+ * @since 1.0
+ */
+final class MethodMap {
+    /**
+     * Keep track of all methods with the same name.
+     */
+    private final Map<String, List<Method>> methodByNameMap = new HashMap<String, List<Method>>();
+
+    /**
+     * Add a method to a list of methods by name. For a particular class we are
+     * keeping track of all the methods with the same name.
+     *
+     * @param method the method.
+     */
+    public synchronized void add(Method method) {
+        String methodName = method.getName();
+
+        List<Method> l = methodByNameMap.get(methodName);
+
+        if (l == null) {
+            l = new ArrayList<Method>();
+            methodByNameMap.put(methodName, l);
+        }
+
+        l.add(method);
+    }
+
+    /**
+     * Return a list of methods with the same name.
+     *
+     * @param key the name.
+     * @return List list of methods.
+     */
+    public synchronized List<Method> get(String key) {
+        return methodByNameMap.get(key);
+    }
+
+    /**
+     * Returns the array of method names accessible in this class.
+     * @return the array of names
+     */
+    public synchronized String[] names() {
+        java.util.Set<String> set = methodByNameMap.keySet();
+        return set.toArray(new String[set.size()]);
+    }
+
+    /**
+     * <p>
+     * Find a method.  Attempts to find the
+     * most specific applicable method using the
+     * algorithm described in the JLS section
+     * 15.12.2 (with the exception that it can't
+     * distinguish a primitive type argument from
+     * an object type argument, since in reflection
+     * primitive type arguments are represented by
+     * their object counterparts, so for an argument of
+     * type (say) java.lang.Integer, it will not be able
+     * to decide between a method that takes int and a
+     * method that takes java.lang.Integer as a parameter.
+     * </p>
+     *
+     * <p>
+     * This turns out to be a relatively rare case
+     * where this is needed - however, functionality
+     * like this is needed.
+     * </p>
+     *
+     * @param methodName name of method
+     * @param args       the actual arguments with which the method is called
+     * @return the most specific applicable method, or null if no
+     *         method is applicable.
+     * @throws MethodKey.AmbiguousException if there is more than one maximally
+     *                            specific applicable method
+     */
+    // CSOFF: RedundantThrows
+    public Method find(String methodName, Object[] args) throws MethodKey.AmbiguousException {
+        return find(new MethodKey(methodName, args));
+    }
+
+    /**
+     * Finds a method by key.
+     * @param methodKey the key
+     * @return the method
+     * @throws MethodKey.AmbiguousException if find is ambiguous
+     */
+    Method find(MethodKey methodKey) throws MethodKey.AmbiguousException {
+        List<Method> methodList = get(methodKey.getMethod());
+        if (methodList == null) {
+            return null;
+        }
+        return methodKey.getMostSpecificMethod(methodList);
+    } // CSON: RedundantThrows
+
+}
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/introspection/package.html b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/introspection/package.html
new file mode 100644
index 0000000..57294df
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/introspection/package.html
@@ -0,0 +1,39 @@
+<html>
+    <!--
+       Licensed to the Apache Software Foundation (ASF) under one or more
+       contributor license agreements.  See the NOTICE file distributed with
+       this work for additional information regarding copyright ownership.
+       The ASF licenses this file to You 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.
+    -->
+    <head>
+        <title>Package Documentation for org.apache.commons.jexl2.introspection Package</title>
+    </head>
+    <body bgcolor="white">
+        Provides low-level introspective services.
+        <p>
+            This internal package is not intended for public usage and there is <b>no</b>
+            guarantee that its public classes or methods will remain as is in subsequent
+            versions.
+        </p>
+        <p>
+            The IntrospectorBase, ClassMap, MethodKey, MethodMap form the
+            base of the introspection service. They allow to describe classes and their
+            methods, keeping them in a cache (@see IntrospectorBase) to speed up property
+            getters/setters and method discovery used during expression evaluation.
+        </p>
+        <p>
+            The cache materialized in Introspector creates one entry per class containing a map of all
+            accessible public methods keyed by name and signature.
+        </p>
+    </body>
+</html>
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/package.html b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/package.html
new file mode 100644
index 0000000..5100b92
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/internal/package.html
@@ -0,0 +1,37 @@
+<html>
+    <!--
+       Licensed to the Apache Software Foundation (ASF) under one or more
+       contributor license agreements.  See the NOTICE file distributed with
+       this work for additional information regarding copyright ownership.
+       The ASF licenses this file to You 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.
+    -->
+    <head>
+        <title>Package Documentation for org.apache.commons.jexl2 Package</title>
+    </head>
+    <body bgcolor="white">
+        <h2>Provides utilities for introspection services.</h2>
+        <p>
+            This internal package is not intended for public usage and there is <b>no</b>
+            guarantee that its public classes or methods will remain as is in subsequent
+            versions.
+        </p>
+        <p>
+            This set of classes implement the various forms of setters and getters
+            used by Jexl. These are specialized forms for 'pure' properties, discovering
+            methods of the {s,g}etProperty form, for Maps, Lists and Ducks -
+            attempting to discover a 'get' or 'set' method, making an object walk and
+            quack.
+        </p>
+
+    </body>
+</html>
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/introspection/JexlMethod.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/introspection/JexlMethod.java
new file mode 100644
index 0000000..ddcfb97
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/introspection/JexlMethod.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2.introspection;
+
+/**
+ * Interface used for regular method invocation.
+ * Ex.
+ * <code>
+ * ${foo.bar()}
+ * </code>
+ * 
+ * @since 1.0
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @version $Id$
+ */
+public interface JexlMethod {
+    /**
+     * Invocation method, called when the method invocation should be performed
+     * and a value returned.
+
+     * @param obj the object
+     * @param params method parameters.
+     * @return the result
+     * @throws Exception on any error.
+     */
+    Object invoke(Object obj, Object[] params) throws Exception;
+
+    /**
+     * Attempts to reuse this JexlMethod, checking that it is compatible with
+     * the actual set of arguments.
+     * Related to isCacheable since this method is often used with cached JexlMethod instances.
+     * @param obj the object to invoke the method upon
+     * @param name the method name
+     * @param params the method arguments
+     * @return the result of the method invocation that should be checked by tryFailed to determine if it succeeded
+     * or failed.
+     */
+    Object tryInvoke(String name, Object obj, Object[] params);
+
+    /**
+     * Checks whether a tryInvoke failed or not.
+     * @param rval the value returned by tryInvoke
+     * @return true if tryInvoke failed, false otherwise
+     */
+    boolean tryFailed(Object rval);
+
+    /**
+     * Specifies if this JexlMethod is cacheable and able to be reused for this
+     * class of object it was returned for.
+     * 
+     * @return true if can be reused for this class, false if not
+     */
+    boolean isCacheable();
+
+    /**
+     * returns the return type of the method invoked.
+     * @return return type
+     */
+    Class<?> getReturnType();
+}
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/introspection/JexlPropertyGet.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/introspection/JexlPropertyGet.java
new file mode 100644
index 0000000..2130c0f
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/introspection/JexlPropertyGet.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2.introspection;
+
+/**
+ * Interface for getting values that appear to be properties.
+ * Ex.
+ * <code>
+ * ${foo.bar}
+ * </code>
+ * @since 1.0
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @version $Id$
+ */
+public interface JexlPropertyGet {
+    /**
+     * Method used to get the property value of an object.
+     * 
+     * @param obj the object to get the property value from.
+     * @return the property value.
+     * @throws Exception on any error.
+     */
+    Object invoke(Object obj) throws Exception;
+
+    /**
+     * Attempts to reuse this JexlPropertyGet, checking that it is compatible with
+     * the actual set of arguments.
+     * @param obj the object to invoke the property get upon
+     * @param key the property key to get
+     * @return the result of the method invocation that should be checked by tryFailed to determine if it succeeded
+     * or failed.
+     */
+    Object tryInvoke(Object obj, Object key);
+
+    /**
+     * Checks whether a tryInvoke failed or not.
+     * @param rval the value returned by tryInvoke
+     * @return true if tryInvoke failed, false otherwise
+     */
+    boolean tryFailed(Object rval);
+
+    /**
+     * Specifies if this JexlPropertyGet is cacheable and able to be reused for
+     * this class of object it was returned for.
+     *
+     * @return true if can be reused for this class, false if not
+     */
+    boolean isCacheable();
+}
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/introspection/JexlPropertySet.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/introspection/JexlPropertySet.java
new file mode 100644
index 0000000..c49f5f1
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/introspection/JexlPropertySet.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2.introspection;
+
+/**
+ * Interface used for setting values that appear to be properties.
+ * Ex.
+ * <code>
+ * ${foo.bar = "hello"}
+ * </code>
+ * @since 1.0
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @version $Id$
+ */
+public interface JexlPropertySet {
+    /**
+     * Method used to set the property value of an object.
+     * 
+     * @param obj Object on which the property setter will be called with the value
+     * @param arg value to be set
+     * @return the value returned from the set operation (impl specific)
+     * @throws Exception on any error.
+     */
+    Object invoke(Object obj, Object arg) throws Exception;
+
+    /**
+     * Attempts to reuse this JexlPropertySet, checking that it is compatible with
+     * the actual set of arguments.
+     * @param obj the object to invoke the the get upon
+     * @param key the property key to get
+     * @param value the property value to set
+     * @return the result of the method invocation that should be checked by tryFailed to determine if it succeeded
+     * or failed.
+     */
+    Object tryInvoke(Object obj, Object key, Object value);
+
+    /**
+     * Checks whether a tryInvoke failed or not.
+     * @param rval the value returned by tryInvoke
+     * @return true if tryInvoke failed, false otherwise
+     */
+    boolean tryFailed(Object rval);
+    
+    /**
+     * Specifies if this JexlPropertySet is cacheable and able to be reused for
+     * this class of object it was returned for.
+     * 
+     * @return true if can be reused for this class, false if not
+     */
+    boolean isCacheable();
+}
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/introspection/Uberspect.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/introspection/Uberspect.java
new file mode 100644
index 0000000..5230fc8
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/introspection/Uberspect.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2.introspection;
+
+import java.util.Iterator;
+import java.lang.reflect.Constructor;
+import org.apache.commons.jexl2.JexlInfo;
+
+/**
+ * 'Federated' introspection/reflection interface to allow the introspection
+ * behavior in JEXL to be customized.
+ * 
+ * @since 1.0
+ * @author <a href="mailto:geirm@apache.org">Geir Magusson Jr.</a>
+ * @version $Id$
+ */
+public interface Uberspect {
+    /** Sets the class loader to use when getting a constructor with
+     * a class name parameter.
+     * @param loader the class loader
+     */
+    void setClassLoader(ClassLoader loader);
+
+    /**
+     * Returns a class constructor.
+     * @param ctorHandle a class or class name
+     * @param args constructor arguments
+     * @param info contextual information
+     * @return a {@link Constructor}
+     */
+    Constructor<?> getConstructor(Object ctorHandle, Object[] args, JexlInfo info);
+    /**
+     * Returns a JexlMethod.
+     * @param obj the object
+     * @param method the method name
+     * @param args method arguments
+     * @param info contextual information
+     * @return a {@link JexlMethod}
+     */
+    JexlMethod getMethod(Object obj, String method, Object[] args, JexlInfo info);
+
+    /**
+     * Property getter.
+     * <p>Returns JexlPropertyGet appropos for ${bar.woogie}.
+     * @param obj the object to get the property from
+     * @param identifier property name
+     * @param info contextual information
+     * @return a {@link JexlPropertyGet}
+     */
+    JexlPropertyGet getPropertyGet(Object obj, Object identifier, JexlInfo info);
+
+    /**
+     * Property setter.
+     * <p>returns JelPropertySet appropos for ${foo.bar = "geir"}</p>.
+     * @param obj the object to get the property from.
+     * @param identifier property name
+     * @param arg value to set
+     * @param info contextual information
+     * @return a {@link JexlPropertySet}.
+     */
+    JexlPropertySet getPropertySet(Object obj, Object identifier, Object arg, JexlInfo info);
+
+    /**
+     * Gets an iterator from an object.
+     * @param obj to get the iterator for
+     * @param info contextual information
+     * @return an iterator over obj
+     */
+    Iterator<?> getIterator(Object obj, JexlInfo info);
+
+}
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/introspection/UberspectImpl.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/introspection/UberspectImpl.java
new file mode 100644
index 0000000..e975727
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/introspection/UberspectImpl.java
@@ -0,0 +1,258 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2.introspection;
+
+import org.apache.commons.jexl2.internal.Introspector;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Enumeration;
+import java.util.Iterator;
+
+import java.util.Map;
+import org.apache.commons.jexl2.JexlInfo;
+import org.apache.commons.jexl2.JexlException;
+import org.apache.commons.jexl2.internal.AbstractExecutor;
+import org.apache.commons.jexl2.internal.ArrayIterator;
+import org.apache.commons.jexl2.internal.EnumerationIterator;
+import org.apache.commons.jexl2.internal.introspection.MethodKey;
+import org.apache.commons.logging.Log;
+
+/**
+ * Implementation of Uberspect to provide the default introspective
+ * functionality of JEXL.
+ * <p>This is the class to derive to customize introspection.</p>
+ *
+ * @since 1.0
+ * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
+ * @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
+ * @version $Id$
+ */
+public class UberspectImpl extends Introspector implements Uberspect {
+    /**
+     * Publicly exposed special failure object returned by tryInvoke.
+     */
+    public static final Object TRY_FAILED = AbstractExecutor.TRY_FAILED;
+    
+    /**
+     * Creates a new UberspectImpl.
+     * @param runtimeLogger the logger used for all logging needs
+     */
+    public UberspectImpl(Log runtimeLogger) {
+        super(runtimeLogger);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @SuppressWarnings("unchecked")
+    public Iterator<?> getIterator(Object obj, JexlInfo info) {
+        if (obj instanceof Iterator<?>) {
+            return ((Iterator<?>) obj);
+        }
+        if (obj.getClass().isArray()) {
+            return new ArrayIterator(obj);
+        }
+        if (obj instanceof Map<?,?>) {
+            return ((Map<?,?>) obj).values().iterator();
+        }
+        if (obj instanceof Enumeration<?>) {
+            return new EnumerationIterator<Object>((Enumeration<Object>) obj);
+        }
+        if (obj instanceof Iterable<?>) {
+            return ((Iterable<?>) obj).iterator();
+        }
+        try {
+            // look for an iterator() method to support the JDK5 Iterable
+            // interface or any user tools/DTOs that want to work in
+            // foreach without implementing the Collection interface
+            AbstractExecutor.Method it = getMethodExecutor(obj, "iterator", null);
+            if (it != null && Iterator.class.isAssignableFrom(it.getReturnType())) {
+                return (Iterator<Object>) it.execute(obj, null);
+            }
+        } catch(Exception xany) {
+            throw new JexlException(info, "unable to generate iterator()", xany);
+        }
+        return null;
+    }
+
+    /**
+     * Returns a class field.
+     * @param obj the object
+     * @param name the field name
+     * @param info debug info
+     * @return a {@link Field}.
+     */
+    public Field getField(Object obj, String name, JexlInfo info) {
+        final Class<?> clazz = obj instanceof Class<?>? (Class<?>) obj : obj.getClass();
+        return getField(clazz, name);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Constructor<?> getConstructor(Object ctorHandle, Object[] args, JexlInfo info) {
+        return getConstructor(ctorHandle, args);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public JexlMethod getMethod(Object obj, String method, Object[] args, JexlInfo info) {
+        return getMethodExecutor(obj, method, args);
+    }
+
+    /**
+     * A JexlPropertyGet for public fields.
+     */
+    public static final class FieldPropertyGet implements JexlPropertyGet {
+        /**
+         * The public field.
+         */
+        private final Field field;
+
+        /**
+         * Creates a new instance of FieldPropertyGet.
+         * @param theField the class public field
+         */
+        public FieldPropertyGet(Field theField) {
+            field = theField;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Object invoke(Object obj) throws Exception {
+            return field.get(obj);
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Object tryInvoke(Object obj, Object key) {
+            if (obj.getClass().equals(field.getDeclaringClass()) && key.equals(field.getName())) {
+                try {
+                    return field.get(obj);
+                } catch (IllegalAccessException xill) {
+                    return TRY_FAILED;
+                }
+            }
+            return TRY_FAILED;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean tryFailed(Object rval) {
+            return rval == TRY_FAILED;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean isCacheable() {
+            return true;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public JexlPropertyGet getPropertyGet(Object obj, Object identifier, JexlInfo info) {
+        JexlPropertyGet get = getGetExecutor(obj, identifier);
+        if (get == null && obj != null && identifier != null) {
+            Field field = getField(obj, identifier.toString(), info);
+            if (field != null) {
+                return new FieldPropertyGet(field);
+            }
+        }
+        return get;
+    }
+
+    /**
+     * A JexlPropertySet for public fields.
+     */
+    public static final class FieldPropertySet implements JexlPropertySet {
+        /**
+         * The public field.
+         */
+        private final Field field;
+
+        /**
+         * Creates a new instance of FieldPropertySet.
+         * @param theField the class public field
+         */
+        public FieldPropertySet(Field theField) {
+            field = theField;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Object invoke(Object obj, Object arg) throws Exception {
+            field.set(obj, arg);
+            return arg;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Object tryInvoke(Object obj, Object key, Object value) {
+            if (obj.getClass().equals(field.getDeclaringClass())
+                && key.equals(field.getName())
+                && (value == null || MethodKey.isInvocationConvertible(field.getType(), value.getClass(), false))) {
+                try {
+                    field.set(obj, value);
+                    return value;
+                } catch (IllegalAccessException xill) {
+                    return TRY_FAILED;
+                }
+            }
+            return TRY_FAILED;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean tryFailed(Object rval) {
+            return rval == TRY_FAILED;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean isCacheable() {
+            return true;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public JexlPropertySet getPropertySet(final Object obj, final Object identifier, Object arg, JexlInfo info) {
+        JexlPropertySet set = getSetExecutor(obj, identifier, arg);
+        if (set == null && obj != null && identifier != null) {
+            Field field = getField(obj, identifier.toString(), info);
+            if (field != null
+                && !Modifier.isFinal(field.getModifiers())
+                && (arg == null || MethodKey.isInvocationConvertible(field.getType(), arg.getClass(), false))) {
+                return new FieldPropertySet(field);
+            }
+        }
+        return set;
+    }
+}
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/introspection/package.html b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/introspection/package.html
new file mode 100644
index 0000000..e8eba16
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/introspection/package.html
@@ -0,0 +1,34 @@
+<html>
+    <!--
+       Licensed to the Apache Software Foundation (ASF) under one or more
+       contributor license agreements.  See the NOTICE file distributed with
+       this work for additional information regarding copyright ownership.
+       The ASF licenses this file to You 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.
+    -->
+    <head>
+        <title>Package Documentation for org.apache.commons.jexl2.introspection Package</title>
+    </head>
+    <body bgcolor="white">
+        <h2>Provides high-level introspective services.</h2>
+        <p>
+            The Uberspect, JexlMethod, JexlPropertyGet and JexlPropertySet interfaces
+            form the exposed face of introspective services.
+        </p>
+        <p>
+            The Uberspectimpl is the concrete class implementing the Uberspect interface.
+            Deriving from this class is the preferred way of augmenting Jexl introspective
+            capabilities when special needs to be fullfilled or when default behaviors
+            need to be modified.
+        </p>
+    </body>
+</html>
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/package.html b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/package.html
new file mode 100644
index 0000000..03aa31d
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/package.html
@@ -0,0 +1,276 @@
+<html>
+    <!--
+       Licensed to the Apache Software Foundation (ASF) under one or more
+       contributor license agreements.  See the NOTICE file distributed with
+       this work for additional information regarding copyright ownership.
+       The ASF licenses this file to You 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.
+    -->
+    <head>
+        <title>Package Documentation for org.apache.commons.jexl2 Package</title>
+    </head>
+    <body bgcolor="white">
+        Provides a framework for evaluating JEXL expressions.
+        <br/><br/>
+        <ul>
+            <li><a href="#intro">Introduction</a></li>
+            <li><a href="#example">Brief Example</a></li>
+            <li><a href="#usage">Using JEXL</a></li>
+            <li><a href="#configuration">Configuring JEXL</a></li>
+            <li><a href="#customization">Customizing JEXL</a></li>
+        </ul>
+
+        <h2><a name="intro">Introduction</a></h2>
+        <p>
+            JEXL is a library intended to facilitate the implementation of dynamic and scripting features in applications
+            and frameworks.
+        </p>
+        
+        <h2><a name="example">A Brief Example</a></h2>
+        <p>
+            When evaluating expressions, JEXL merges an
+            {@link org.apache.commons.jexl2.Expression}
+            with a
+            {@link org.apache.commons.jexl2.JexlContext}.
+            An Expression is created using
+            {@link org.apache.commons.jexl2.JexlEngine#createExpression(java.lang.String)},
+            passing a String containing valid JEXL syntax.  A simple JexlContext can be created using
+            a {@link org.apache.commons.jexl2.MapContext} instance;
+            a map of variables that will be internally wrapped can be optionally provided through its constructor.
+            The following example, takes a variable named foo, and
+            invokes the bar() method on the property innerFoo:
+        </p>
+        <pre>
+            // Create a JexlEngine (could reuse one instead)
+            JexlEngine jexl = new JexlEngine();
+            // Create an expression object
+            String jexlExp = "foo.innerFoo.bar()";
+            Expression e = jexl.createExpression( jexlExp );
+
+            // Create a context and add data
+            JexlContext jc = new MapContext();
+            jc.set("foo", new Foo() );
+
+            // Now evaluate the expression, getting the result
+            Object o = e.evaluate(jc);
+        </pre>
+
+
+        <h2><a name="usage">Using JEXL</a></h2>
+        The API is composed of three levels addressing different functional needs:
+        <ul>
+            <li>Dynamic invocation of setters, getters, methods and constructors</li>
+            <li>Script expressions known as JEXL expressions</li>
+            <li>JSP/JSF like expression known as UnifiedJEXL expressions</li>
+        </ul>
+
+        <h3><a name="usage_note">Important note</a></h3>
+            The only public packages you should use are:
+        <ul>
+            <li>org.apache.commons.jexl2</li>
+            <li>org.apache.commons.jexl2.introspection</li>
+        </ul>
+            The following packages follow a "use at your own maintenance cost" policy.
+            Their classes and methods are not guaranteed to remain compatible in subsequent versions.
+            If you think you need to use some of their features, it might be a good idea to check with
+            the community through the mailing list first.
+       <ul>
+            <li>org.apache.commons.jexl2.parser</li>
+            <li>org.apache.commons.jexl2.scripting</li>
+            <li>org.apache.commons.jexl2.internal</li>
+            <li>org.apache.commons.jexl2.internal.introspection</li>
+        </ul>
+
+        <h3><a name="usage_api">Dynamic invocation</a></h3>
+        <p>
+            These functionalities are close to the core level utilities found in
+            <a href="http://commons.apache.org/beanutils/">BeanUtils</a>.
+            For basic dynamic property manipulations and method invocation, you can use the following
+            set of methods:
+        </p>
+        <ul>
+            <li>{@link org.apache.commons.jexl2.JexlEngine#newInstance}</li>
+            <li>{@link org.apache.commons.jexl2.JexlEngine#setProperty}</li>
+            <li>{@link org.apache.commons.jexl2.JexlEngine#getProperty}</li>
+            <li>{@link org.apache.commons.jexl2.JexlEngine#invokeMethod}</li>
+        </ul>
+        The following example illustrate their usage:
+        <pre>
+            // test outer class
+            public static class Froboz {
+                int value;
+                public Froboz(int v) { value = v; }
+                public void setValue(int v) { value = v; }
+                public int getValue() { return value; }
+            }
+            // test inner class
+            public static class Quux {
+                String str;
+                Froboz froboz;
+                public Quux(String str, int fro) {
+                    this.str = str;
+                    froboz = new Froboz(fro);
+                }
+                public Froboz getFroboz() { return froboz; }
+                public void setFroboz(Froboz froboz) { this.froboz = froboz; }
+                public String getStr() { return str; }
+                public void setStr(String str) { this.str = str; }
+            }
+            // test API
+            JexlEngine jexl = nex JexlEngine();
+            Quux quux = jexl.newInstance(Quux.class, "xuuq", 100);
+            jexl.setProperty(quux, "froboz.value", Integer.valueOf(100));
+            Object o = jexl.getProperty(quux, "froboz.value");
+            assertEquals("Result is not 100", new Integer(100), o);
+            jexl.setProperty(quux, "['froboz'].value", Integer.valueOf(1000));
+            o = jexl.getProperty(quux, "['froboz']['value']");
+            assertEquals("Result is not 1000", new Integer(1000), o);
+        </pre>
+
+        <h3><a name="usage_jexl">JEXL script expression</a></h3>
+        <p>
+            If your needs require simple expression evaluation capabilities, the core JEXL features
+            will most likely fit.
+            The main methods are:
+        </p>
+        <ul>
+            <li>{@link org.apache.commons.jexl2.JexlEngine#createExpression}</li>
+            <li>{@link org.apache.commons.jexl2.JexlEngine#createScript}</li>
+            <li>{@link org.apache.commons.jexl2.Expression#evaluate}</li>
+        </ul>
+        The following example illustrates their usage:
+        <pre>
+            JexlEngine jexl = nex JexlEngine();
+
+            JexlContext jc = new MapContext();
+            jc.set("quuxClass", quux.class);
+
+            Expression create = jexl.createExpression("quux = new(quuxClass, 'xuuq', 100)");
+            Expression assign = jexl.createExpression("quux.froboz.value = 10");
+            Expression check = jexl.createExpression("quux[\"froboz\"].value");
+            Quux quux = (Quux) create.evaluate(jc);
+            Object o = assign.evaluate(jc);
+            assertEquals("Result is not 10", new Integer(10), o);
+            o = check.evaluate(jc);
+            assertEquals("Result is not 10", new Integer(10), o);
+        </pre>
+
+        <h3><a name="usage_ujexl">UnifiedJEXL script expressions</a></h3>
+        <p>
+            If you are looking for JSP-EL like and basic templating features, you can
+            use UnifiedJEXL.
+        </p>
+        The main methods are:
+        <ul>
+            <li>{@link org.apache.commons.jexl2.UnifiedJEXL#parse}</li>
+            <li>{@link org.apache.commons.jexl2.UnifiedJEXL.Expression#evaluate}</li>
+            <li>{@link org.apache.commons.jexl2.UnifiedJEXL.Expression#prepare}</li>
+        </ul>
+        The following example illustrates their usage:
+        <pre>
+            JexlEngine jexl = new JexlEngine();
+            UnifiedJEXL ujexl = new UnifiedJEXL(jexl);
+            UnifiedJEXL.Expression expr = ujexl.parse("Hello ${user}");
+            String hello = expr.evaluate(context, expr).toString();
+        </pre>
+
+        <h2><a name="configuration">JEXL Configuration</a></h2>
+        <p>
+            The JexlEngine can be configured through a few parameters that will drive how it reacts
+            in case of errors. These configuration methods are best called at JEXL engine initialization time; it
+            is recommended to derive from JexlEngine to call those in a constructor.
+        </p>
+        <p>
+            {@link org.apache.commons.jexl2.JexlEngine#setLenient} configures when JEXL considers 'null' as an error or not in various situations;
+            when facing an unreferenceable variable, using null as an argument to an arithmetic operator or failing to call
+            a method or constructor. The lenient mode is close to JEXL-1.1 behavior.
+        </p>
+        <p>
+            {@link org.apache.commons.jexl2.JexlEngine#setSilent} configures how JEXL reacts to errors; if silent, the engine will not throw exceptions
+            but will warn through loggers and return null in case of errors. Note that when non-silent, JEXL throws
+            JexlException which are unchecked exception.
+        </p>
+        <p>
+           {@link org.apache.commons.jexl2.JexlEngine#setDebug} makes stacktraces carried by JExlException more meaningfull; in particular, these
+            traces will carry the exact caller location the Expression was created from.
+        </p>
+        <p>
+        {@link org.apache.commons.jexl2.JexlEngine#setClassLoader} indicates to a JexlEngine which class loader to use to solve a class name; this affects
+        how JexlEngine.newInstance and the 'new' script method operates. This is mostly usefull in cases where
+        you rely on JEXL to dynamically load and call plugins for your application.
+        </p>
+        <p>
+            JexlEngine and UnifiedJEXL expression caches can be configured as well. If you intend to use JEXL
+            repeatedly in your application, these are worth configuring since expression parsing is quite heavy.
+            Note that all caches created by JEXL are held through SoftReference; under high memory pressure, the GC will be able
+            to reclaim those caches and JEXL will rebuild them if needed. By default, a JexlEngine does not create a cache
+            whilst UnifiedJEXL does.
+        </p>
+        <p>Both JexlEngine and UnifiedJEXL are thread-safe; the same instance can be shared between different
+            threads and proper synchronization is enforced in critical areas.</p>
+        <p>{@link org.apache.commons.jexl2.JexlEngine#setCache} will set how many expressions can be simultaneously cached by the
+            JEXL engine. UnifiedJEXL allows to define the cache size through its constructor.</p>
+        <p>
+            {@link org.apache.commons.jexl2.JexlEngine#setFunctions} extends JEXL scripting by registering functions in
+            namespaces.
+        </p>
+            This can be used as in:
+        <pre><code>
+            public static MyMath {
+                public double cos(double x) {
+                    return Math.cos(x);
+                }
+            }
+            Map&lt;String, Object> funcs = new HashMap&lt;String, Object>();
+            funcs.put("math", new MyMath());
+            JexlEngine jexl = new JexlEngine();
+            jexl.setFunctions(funcs);
+
+            JexlContext jc = new MapContext();
+            jc.set("pi", Math.PI);
+
+            e = JEXL.createExpression("math:cos(pi)");
+            o = e.evaluate(jc);
+            assertEquals(Double.valueOf(-1),o);
+        </code></pre>
+            If the <i>namespace</i> is a Class and that class declares a constructor that takes a JexlContext (or
+            a class extending JexlContext), one <i>namespace</i> instance is created on first usage in an
+            expression; this instance lifetime is limited to the expression evaluation.
+
+        <h2><a name="customization">JEXL Customization</a></h2>
+        If you need to make JEXL treat some objects in a specialized manner or tweak how it
+        reacts to some settings, you can derive most of its inner-workings.
+        <p>
+           {@link org.apache.commons.jexl2.JexlEngine} is meant to be
+           extended and lets you capture your own configuration defaults wrt cache sizes and various flags.
+           Implementing your own cache - instead of the basic LinkedHashMap based one - would be
+           another possible extension.
+        </p>
+        <p>
+            {@link org.apache.commons.jexl2.JexlArithmetic}
+            is the class to derive if you need to change how operators behave. For example, this would
+            be the case if you wanted '+' to operate on arrays; you'd need to derive JexlArithmetic and
+            implement your own version of Add.
+        </p>
+        <p>
+            {@link org.apache.commons.jexl2.Interpreter}
+            is the class to derive if you need to add more features to the evaluation
+            itself; for instance, you want pre- and post- resolvers for variables or nested scopes for
+            for variable contexts or add factory based support to the 'new' operator.
+        </p>
+        <p>
+            {@link org.apache.commons.jexl2.introspection.UberspectImpl}
+            is the class to derive if you need to add introspection or reflection capabilities for some objects.
+            The code already reflects public fields as properties on top of Java-beans conventions.
+        </p>
+    </body>
+</html>
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/parser/JexlNode.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/parser/JexlNode.java
new file mode 100644
index 0000000..06659d6
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/parser/JexlNode.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2.parser;
+
+import org.apache.commons.jexl2.JexlInfo;
+
+/**
+ * Base class for parser nodes - holds an 'image' of the token for later use.
+ *
+ * @since 2.0
+ */
+public abstract class JexlNode extends SimpleNode implements JexlInfo {
+    /** token value. */
+    public String image;
+
+    public JexlNode(int id) {
+        super(id);
+    }
+
+    public JexlNode(Parser p, int id) {
+        super(p, id);
+    }
+
+    public JexlInfo getInfo() {
+        JexlNode node = this;
+        while (node != null) {
+            if (node.value instanceof JexlInfo) {
+                return (JexlInfo) node.value;
+            }
+            node = node.jjtGetParent();
+        }
+        return null;
+    }
+    
+    /** {@inheritDoc} */
+    public String debugString() {
+        JexlInfo info = getInfo();
+        return info != null? info.debugString() : "";
+    }
+}
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/parser/Parser.jjt b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/parser/Parser.jjt
new file mode 100644
index 0000000..e0e0626
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/parser/Parser.jjt
@@ -0,0 +1,564 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+/**
+ *  Jexl : Java Expression Language
+ *
+ *  @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
+ *  @author <a href="mailto:mhw@kremvax.net">Mark H. Wilkinson</a>
+ *
+ *  @version $Id$
+ */
+
+options
+{
+   MULTI=true;
+   STATIC=false;
+   VISITOR=true;
+   NODE_SCOPE_HOOK=true;
+   NODE_CLASS="JexlNode";
+   UNICODE_INPUT=true;
+}
+
+PARSER_BEGIN(Parser)
+
+package org.apache.commons.jexl2.parser;
+
+import java.io.Reader;
+import org.apache.commons.jexl2.JexlInfo;
+
+public class Parser extends StringParser
+{
+    public ASTJexlScript parse(Reader reader, JexlInfo info)
+        throws ParseException
+    {
+        ReInit(reader);
+        /*
+         *  lets do the 'Unique Init' in here to be
+         *  safe - it's a pain to remember
+         */
+
+        ASTJexlScript tree = JexlScript();
+        tree.value = info;
+        return tree;
+    }
+
+    void jjtreeOpenNodeScope(Node n) {}
+    void jjtreeCloseNodeScope(Node n) throws ParseException {
+      if (n instanceof ASTAmbiguous && n.jjtGetNumChildren() > 0) {
+          Token tok = this.getToken(0);
+          StringBuilder strb = new StringBuilder("Ambiguous statement ");
+          if (tok != null) {
+              strb.append("@");
+              strb.append(tok.beginLine);
+              strb.append(":");
+              strb.append(tok.beginColumn);
+          }
+          strb.append(", missing ';' between expressions");
+         throw new ParseException(strb.toString());
+      }
+    }
+}
+
+PARSER_END(Parser)
+
+
+/***************************************
+ *     Skip & Number literal tokens
+ ***************************************/
+
+<*> SKIP : /* WHITE SPACE */
+{
+  <"##" (~["\n","\r"])* ("\n" | "\r" | "\r\n")? >
+| <"/*" (~["*"])* "*" ("*" | ~["*","/"] (~["*"])* "*")* "/">
+| <"//" (~["\n","\r"])* ("\n" | "\r" | "\r\n")? >
+| " "
+| "\t"
+| "\n"
+| "\r"
+| "\f"
+}
+
+<*> TOKEN : /* LITERALS */
+{
+  < INTEGER_LITERAL: (<DIGIT>)+ >
+|
+  < FLOAT_LITERAL: (<DIGIT>)+ "."(<DIGIT>)+ >
+}
+
+
+<*> TOKEN : /* KEYWORDS */
+{
+      < IF : "if" >
+    | < ELSE : "else" >
+    | < FOR : "for" >
+    | < FOREACH : "foreach" > : FOR_EACH_IN
+    | < WHILE : "while" >
+    | < NEW : "new" >
+    | < EMPTY : "empty" >
+    | < SIZE : "size" >
+    | < NULL : "null" >
+    | < TRUE : "true" >
+    | < FALSE : "false" >
+}
+
+<*> TOKEN : { /* SEPARATORS */
+      < LPAREN : "(" >
+    | < RPAREN : ")" >
+    | < LCURLY : "{" >
+    | < RCURLY : "}" >
+    | < LBRACKET : "[" >
+    | < RBRACKET : "]" >
+    | < SEMICOL : ";" >
+    | < COLON : ":" >
+    | < COMMA : "," >
+}
+
+<FOR_EACH_IN> TOKEN : /* foreach in */
+{
+    < IN : "in" > : DEFAULT
+}
+
+/***************************************
+ *      Statements
+ ***************************************/
+
+ASTJexlScript JexlScript() : {}
+{
+   ( Statement() )* <EOF>
+   { return jjtThis;}
+}
+
+
+void Statement() #void : {}
+{
+    <SEMICOL>
+    | LOOKAHEAD(3) Block()
+    | IfStatement()
+    | ForeachStatement()
+    | WhileStatement()
+    | ExpressionStatement()
+}
+
+void Block() #Block : {}
+{
+    <LCURLY> ( Statement() )* <RCURLY>
+}
+
+
+void ExpressionStatement() #void : {}
+{
+    Expression() (LOOKAHEAD(1) Expression() #Ambiguous())* (LOOKAHEAD(2) <SEMICOL>)?
+}
+
+
+void IfStatement() : {}
+{
+    <IF> <LPAREN> Expression() <RPAREN> Statement() ( LOOKAHEAD(1) <ELSE> Statement() )?
+}
+
+
+void WhileStatement() : {}
+{
+    <WHILE> <LPAREN> Expression() <RPAREN> Statement()
+}
+
+
+void ForeachStatement() : {}
+{
+    <FOR> <LPAREN> Reference() <COLON>  Expression() <RPAREN> Statement()
+|
+    <FOREACH> <LPAREN> Reference() <IN>  Expression() <RPAREN> Statement()
+}
+
+
+/***************************************
+ *      Expression syntax
+ ***************************************/
+
+void Expression() #void : {}
+{
+    LOOKAHEAD( Reference() "=" )  Assignment()
+|
+    ConditionalExpression()
+}
+
+
+void Assignment() #Assignment(2) : {}
+{
+    Reference() "=" Expression()
+}
+
+/***************************************
+ *      Conditional & relational
+ ***************************************/
+
+void ConditionalExpression() #void : {}
+{
+  ConditionalOrExpression()
+  (
+    "?" Expression() <COLON> Expression() #TernaryNode(3)
+  |
+    "?:" Expression() #TernaryNode(2)
+  )?
+}
+
+void ConditionalOrExpression() #void :
+{}
+{
+  ConditionalAndExpression()
+  (
+    "||" ConditionalAndExpression() #OrNode(2)
+  |
+    "or" ConditionalAndExpression() #OrNode(2)
+  )*
+}
+
+void ConditionalAndExpression() #void :
+{}
+{
+  InclusiveOrExpression()
+  (
+    "&&" InclusiveOrExpression() #AndNode(2)
+  |
+    "and" InclusiveOrExpression() #AndNode(2)
+  )*
+}
+
+void InclusiveOrExpression() #void :
+{}
+{
+  ExclusiveOrExpression()
+  ( "|" ExclusiveOrExpression() #BitwiseOrNode(2) )*
+}
+
+void ExclusiveOrExpression() #void :
+{}
+{
+  AndExpression()
+  ( "^" AndExpression() #BitwiseXorNode(2) )*
+}
+
+void AndExpression() #void :
+{}
+{
+  EqualityExpression()
+  ( "&" EqualityExpression() #BitwiseAndNode(2) )*
+}
+
+void EqualityExpression() #void :
+{}
+{
+  RelationalExpression()
+  (
+     "==" RelationalExpression() #EQNode(2)
+   |
+     "eq" RelationalExpression() #EQNode(2)
+   |
+     "!=" RelationalExpression() #NENode(2)
+   |
+     "ne" RelationalExpression() #NENode(2)
+  )?
+}
+
+void RelationalExpression() #void :
+{}
+{
+  AdditiveExpression()
+  (
+    "<" AdditiveExpression() #LTNode(2)
+   |
+    "lt" AdditiveExpression() #LTNode(2)
+   |
+    ">" AdditiveExpression() #GTNode(2)
+   |
+    "gt" AdditiveExpression() #GTNode(2)
+   |
+    "<=" AdditiveExpression() #LENode(2)
+   |
+    "le" AdditiveExpression() #LENode(2)
+   |
+    ">=" AdditiveExpression() #GENode(2)
+   |
+    "ge" AdditiveExpression() #GENode(2)
+   |
+    "=~" AdditiveExpression() #ERNode(2) // equals regexp
+   |
+    "!~" AdditiveExpression() #NRNode(2) // not equals regexp
+  )?
+}
+
+/***************************************
+ *      Arithmetic
+ ***************************************/
+
+void AdditiveExpression() #AdditiveNode(>1) : {}
+{
+    MultiplicativeExpression() ( LOOKAHEAD(1) AdditiveOperator() MultiplicativeExpression())*
+}
+
+void AdditiveOperator() : {}
+{
+    "+" { jjtThis.image = "+"; }
+|
+    "-" { jjtThis.image = "-"; }
+}
+
+void MultiplicativeExpression() #void : {}
+{
+  UnaryExpression()
+  (
+    "*" UnaryExpression() #MulNode(2)
+   |
+    "/" UnaryExpression() #DivNode(2)
+   |
+    "div" UnaryExpression() #DivNode(2)
+   |
+    "%" UnaryExpression() #ModNode(2)
+   |
+    "mod" UnaryExpression() #ModNode(2)
+  )*
+}
+
+void UnaryExpression() #void : {}
+{
+  "-" UnaryExpression() #UnaryMinusNode(1)
+|
+  "~" UnaryExpression() #BitwiseComplNode(1)
+|
+  "!" UnaryExpression() #NotNode(1)
+|
+  "not" UnaryExpression() #NotNode(1)
+|
+  PrimaryExpression()
+}
+
+
+/***************************************
+ *      Identifier & Literals
+ ***************************************/
+
+void Identifier() :
+{
+    Token t;
+}
+{
+    t=<IDENTIFIER>
+    { jjtThis.image = t.image; }
+}
+
+void Literal() #void :
+{
+   Token t;
+}
+{
+  IntegerLiteral()
+|
+  FloatLiteral()
+|
+  BooleanLiteral()
+|
+  StringLiteral()
+|
+  NullLiteral()
+}
+
+void NullLiteral() : {}
+{
+    <NULL>
+}
+
+void BooleanLiteral() #void :
+{}
+{
+  <TRUE> #TrueNode
+|
+  <FALSE> #FalseNode
+}
+
+void IntegerLiteral() :
+{
+  Token t;
+}
+{
+  t=<INTEGER_LITERAL>
+  { jjtThis.image = t.image; }
+}
+
+void FloatLiteral() :
+{
+  Token t;
+}
+{
+  t=<FLOAT_LITERAL>
+  { jjtThis.image = t.image; }
+}
+
+void StringLiteral() :
+{
+   Token t;
+}
+{
+  t=<STRING_LITERAL>
+  { jjtThis.image = Parser.buildString(t.image, true); }
+}
+
+void ArrayLiteral() : {}
+{
+  <LBRACKET> Expression() ( <COMMA> Expression() )* <RBRACKET>
+}
+
+void MapLiteral() : {}
+{
+    <LCURLY>  MapEntry() ( <COMMA> MapEntry() )* <RCURLY>
+}
+
+void MapEntry() : {}
+{
+    Expression() <COLON> Expression()
+}
+
+
+/***************************************
+ *      Functions & Methods
+ ***************************************/
+
+void EmptyFunction() : {}
+{
+    LOOKAHEAD(3) <EMPTY> <LPAREN> Expression() <RPAREN>
+|
+    <EMPTY> Reference()
+}
+
+void SizeFunction() : {}
+{
+    <SIZE> <LPAREN> Expression() <RPAREN>
+}
+
+void Function() #FunctionNode: {}
+{
+
+   Identifier() <COLON> Identifier() <LPAREN>[ Expression() ( <COMMA> Expression() )* ] <RPAREN>
+}
+
+void Method() #MethodNode: {}
+{
+   Identifier() <LPAREN>[ Expression() ( <COMMA> Expression() )* ] <RPAREN>
+}
+
+void AnyMethod() #void : {}
+{
+    LOOKAHEAD(<SIZE>) SizeMethod()
+  |
+    LOOKAHEAD(Identifier() <LPAREN>) Method()
+}
+
+void SizeMethod() : {}
+{
+    <SIZE> <LPAREN> <RPAREN>
+}
+
+void Constructor() #ConstructorNode() : {}
+{
+  <NEW> <LPAREN>[ Expression() ( <COMMA> Expression() )* ] <RPAREN>
+}
+
+
+/***************************************
+ *     References
+ ***************************************/
+
+void PrimaryExpression() #void : {}
+{
+  Literal()
+|
+  LOOKAHEAD(3)  Reference()
+|
+  LOOKAHEAD( <LPAREN> ) <LPAREN> Expression() <RPAREN>
+|
+  LOOKAHEAD( <EMPTY> ) EmptyFunction()
+|
+  LOOKAHEAD( <SIZE> ) SizeFunction()
+|
+  LOOKAHEAD( <NEW> <LPAREN> ) Constructor()
+|
+  LOOKAHEAD( <LCURLY> MapEntry() ) MapLiteral()
+|
+  LOOKAHEAD( <LBRACKET> Expression() ) ArrayLiteral()
+}
+
+
+void ArrayAccess() : {}
+{
+    Identifier() (LOOKAHEAD(2) <LBRACKET> Expression() <RBRACKET>)+
+}
+
+void DotReference() #void : {}
+{
+  ("."
+     ( LOOKAHEAD(Identifier() <LBRACKET> )
+         ArrayAccess()
+       |
+       ( LOOKAHEAD(3)
+            AnyMethod()
+          |
+            Identifier()
+          |
+            IntegerLiteral()
+        )
+      )
+  )*
+}
+
+void Reference() : {}
+{
+  ( LOOKAHEAD(<NEW>) Constructor()
+|
+   LOOKAHEAD(Identifier() <LBRACKET> ) ArrayAccess()
+|
+   LOOKAHEAD(Identifier() <COLON> Identifier() <LPAREN>) Function()
+|
+   LOOKAHEAD(Identifier() <LPAREN>) Method()
+|
+   Identifier()
+|
+   LOOKAHEAD(<LCURLY>) MapLiteral()
+|
+   LOOKAHEAD(<LBRACKET>) ArrayLiteral() ) DotReference()
+}
+
+/***************************************
+ *     Identifier & String tokens
+ ***************************************/
+
+<*> TOKEN : /* IDENTIFIERS */
+{
+  < IDENTIFIER: <LETTER> (<LETTER>|<DIGIT>)* >
+|
+  < #LETTER: [ "a"-"z", "A"-"Z", "_", "$" ] >
+|
+  < #DIGIT: [ "0"-"9"] >
+}
+
+<*> TOKEN :
+{
+    <STRING_LITERAL :
+        ("\"" ( ~["\"","\n","\r"] | "\\" ["u","n","t","b","r","f","\\","\""] )* "\"" )
+     |
+        ("\'" ( ~["\'","\n","\r"] | "\\" ["u","n","t","b","r","f","\\","\'"])* "\'" )
+    >
+}
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/parser/SimpleNode.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/parser/SimpleNode.java
new file mode 100644
index 0000000..3b3cb4b
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/parser/SimpleNode.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2.parser;
+
+/**
+ * A class originally generated by JJTree:
+ * /* JavaCCOptions:MULTI=true,NODE_USES_PARSER=true,VISITOR=true,TRACK_TOKENS=false,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY= *\/
+ * Worksaround issue https://javacc.dev.java.net/issues/show_bug.cgi?id=227
+ * As soon as this issue if fixed and the maven plugin uses the correct version of Javacc, this
+ * class can go away.
+ *
+ * The technical goal is to ensure every reference made in the parser was to a JexlNode; unfortunately,
+ * as in javacc 4.1, it still uses a SimpleNode reference in the generated ParserVisitor.
+ * Besides, there is no need to keep the parser around in the node.
+ *
+ * The functional goal is to a allow a <em>volatile</em> value in the node
+ * so it can serve as a last evaluation cache even in multi-threaded executions.
+ */
+public class SimpleNode implements Node {
+  protected JexlNode parent;
+  protected JexlNode[] children;
+  protected int id;
+  /** volatile value so it can be used as a last evaluation cache. */
+  protected volatile Object value;
+
+  public SimpleNode(int i) {
+    id = i;
+  }
+
+  public SimpleNode(Parser p, int i) {
+    this(i);
+  }
+
+  public void jjtOpen() {
+  }
+
+  public void jjtClose() {
+  }
+
+  public void jjtSetParent(Node n) {
+      parent = (JexlNode) n;
+  }
+  
+  public JexlNode jjtGetParent() {
+      return parent;
+  }
+
+  public void jjtAddChild(Node n, int i) {
+    if (children == null) {
+      children = new JexlNode[i + 1];
+    } else if (i >= children.length) {
+      JexlNode c[] = new JexlNode[i + 1];
+      System.arraycopy(children, 0, c, 0, children.length);
+      children = c;
+    }
+    children[i] = (JexlNode) n;
+  }
+
+  public JexlNode jjtGetChild(int i) {
+    return children[i];
+  }
+
+  public int jjtGetNumChildren() {
+    return (children == null) ? 0 : children.length;
+  }
+
+  public void jjtSetValue(Object value) {
+      this.value = value;
+  }
+
+  public Object jjtGetValue() {
+      return value;
+  }
+
+  /** Accept the visitor. **/
+  public Object jjtAccept(ParserVisitor visitor, Object data) {
+    return visitor.visit(this, data);
+  }
+
+  /** Accept the visitor. **/
+  public Object childrenAccept(ParserVisitor visitor, Object data) {
+    if (children != null) {
+      for (int i = 0; i < children.length; ++i) {
+        children[i].jjtAccept(visitor, data);
+      }
+    }
+    return data;
+  }
+
+  /* You can override these two methods in subclasses of SimpleNode to
+     customize the way the JexlNode appears when the tree is dumped.  If
+     your output uses more than one line you should override
+     toString(String), otherwise overriding toString() is probably all
+     you need to do. */
+
+  @Override
+  public String toString() { return ParserTreeConstants.jjtNodeName[id]; }
+  public String toString(String prefix) { return prefix + toString(); }
+
+  /* Override this method if you want to customize how the JexlNode dumps
+     out its children. */
+
+  public void dump(String prefix) {
+    System.out.println(toString(prefix));
+    if (children != null) {
+      for (int i = 0; i < children.length; ++i) {
+  SimpleNode n = children[i];
+  if (n != null) {
+    n.dump(prefix + " ");
+  }
+      }
+    }
+  }
+}
+
+/* JavaCC - OriginalChecksum=7dff880883d088a37c1e3197e4b455a0 (do not edit this line) */
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/parser/StringParser.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/parser/StringParser.java
new file mode 100644
index 0000000..8ade6fd
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/parser/StringParser.java
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2.parser;
+
+/**
+ * Common constant strings utilities.
+ * <p>
+ * This package methods read JEXL string literals and handle escaping through the
+ * 'backslash' (ie: \) character. Escaping is used to neutralize string delimiters (the single
+ * and double quotes) and read Unicode hexadecimal encoded characters.
+ * </p>
+ * <p>
+ * The only escapable characters are the single and double quotes - ''' and '"' -,
+ * a Unicode sequence starting with 'u' followed by 4 hexadecimals and
+ * the backslash character - '\' - itself.
+ * </p>
+ * <p>
+ * A sequence where '\' occurs before any non-escapable character or sequence has no effect, the
+ * sequence output being the same as the input.
+ * </p>
+ */
+public class StringParser {
+    /** Default constructor.  */
+    public StringParser() {}
+    
+    /**
+     * Builds a string, handles escaping through '\' syntax.
+     * @param str the string to build from
+     * @param eatsep whether the separator, the first character, should be considered
+     * @return the built string
+     */
+    public static String buildString(CharSequence str, boolean eatsep) {
+        StringBuilder strb = new StringBuilder(str.length());
+        char sep = eatsep ? str.charAt(0) : 0;
+        int end = str.length() - (eatsep ? 1 : 0);
+        int begin = (eatsep ? 1 : 0);
+        read(strb, str, begin, end, sep);
+        return strb.toString();
+    }
+
+    /**
+     * Read the remainder of a string till a given separator,
+     * handles escaping through '\' syntax.
+     * @param strb the destination buffer to copy characters into
+     * @param str the origin
+     * @param index the offset into the origin
+     * @param sep the separator, single or double quote, marking end of string
+     * @return the offset in origin
+     */
+    public static int readString(StringBuilder strb, CharSequence str, int index, char sep) {
+        return read(strb, str, index, str.length(), sep);
+    }
+
+    /**
+     * Read the remainder of a string till a given separator,
+     * handles escaping through '\' syntax.
+     * @param strb the destination buffer to copy characters into
+     * @param str the origin
+     * @param begin the relative offset in str to begin reading
+     * @param end the relative offset in str to end reading
+     * @param sep the separator, single or double quote, marking end of string
+     * @return the last character offset handled in origin
+     */
+    private static int read(StringBuilder strb, CharSequence str, int begin, int end, char sep) {
+        boolean escape = false;
+        int index = begin;
+        for (; index < end; ++index) {
+            char c = str.charAt(index);
+            if (escape) {
+                if (c == 'u' && (index + 4) < end && readUnicodeChar(strb, str, index + 1) > 0) {
+                    index += 4;
+                }
+                else {
+                    // if c is not an escapable character, re-emmit the backslash before it
+                    boolean notSeparator = sep == 0? c != '\'' && c != '"' : c != sep;
+                    if (notSeparator && c != '\\' ) {
+                        strb.append('\\');
+                    }
+                    strb.append(c);
+                }
+                escape = false;
+                continue;
+            }
+            if (c == '\\') {
+                escape = true;
+                continue;
+            }
+            strb.append(c);
+            if (c == sep) {
+                break;
+            }
+        }
+        return index;
+    }
+
+    /**
+     * Reads a Unicode escape character.
+     * @param strb the builder to write the character to
+     * @param str the sequence
+     * @param begin the begin offset in sequence (after the '\\u')
+     * @return 0 if char could not be read, 4 otherwise
+     */
+    private static final int readUnicodeChar(StringBuilder strb, CharSequence str, int begin) {
+        char xc = 0;
+        int bits = 12;
+        int value = 0;
+        for(int offset = 0; offset < 4; ++offset) {
+            char c = str.charAt(begin + offset);
+            if (c >= '0' && c <= '9') {
+                value = (c - '0');
+            }
+            else if (c >= 'a' && c <= 'h') {
+               value = (c - 'a' + 10);
+            }
+            else if (c >= 'A' && c <= 'H') {
+                value = (c - 'A' + 10);
+            }
+            else {
+                return 0;
+            }
+            xc |= value << bits;
+            bits -= 4;
+        }
+        strb.append(xc);
+        return 4;
+    }
+    
+    /**
+     * Escapes a String representation, expand non-ASCII characters as Unicode escape sequence.
+     * @param str the string to escape
+     * @return the escaped representation
+     */
+    public static String escapeString(String str) {
+        if (str == null) {
+            return null;
+        }
+        final int length = str.length();
+        StringBuilder strb = new StringBuilder(length + 2);
+        strb.append('\'');
+        for (int i = 0; i < length; ++i) {
+            char c = str.charAt(i);
+            if (c < 127) {
+                if (c == '\'') {
+                    // escape quote
+                    strb.append('\\');
+                    strb.append('\'');
+                } else if (c == '\\') {
+                    // escape backslash
+                    strb.append('\\');
+                    strb.append('\\');
+                } else {
+                    strb.append(c);
+                }
+            } else {
+                // convert to Unicode escape sequence
+                strb.append('\\');
+                strb.append('u');
+                String hex = Integer.toHexString(c);
+                for (int h = hex.length(); h < 4; ++h) {
+                    strb.append('0');
+                }
+                strb.append(hex);
+            }
+        }
+        strb.append('\'');
+        return strb.toString();
+    }
+}
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/parser/package.html b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/parser/package.html
new file mode 100644
index 0000000..b09c836
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/parser/package.html
@@ -0,0 +1,31 @@
+<html>
+    <!--
+       Licensed to the Apache Software Foundation (ASF) under one or more
+       contributor license agreements.  See the NOTICE file distributed with
+       this work for additional information regarding copyright ownership.
+       The ASF licenses this file to You 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.
+    -->
+    <head>
+        <title>Package Documentation for org.apache.commons.jexl2.parser Package</title>
+    </head>
+    <body bgcolor="white">
+        <p>
+            Contains the Parser for JEXL script.
+        </p>
+        <p>
+            This internal package is not intended for public usage and there is <b>no</b>
+            guarantee that its public classes or methods will remain as is in subsequent
+            versions.
+        </p>
+    </body>
+</html>
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/scripting/JexlScriptEngine.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/scripting/JexlScriptEngine.java
new file mode 100644
index 0000000..764e9d5
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/scripting/JexlScriptEngine.java
@@ -0,0 +1,376 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2.scripting;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.Writer;
+
+import javax.script.AbstractScriptEngine;
+import javax.script.Bindings;
+import javax.script.Compilable;
+import javax.script.CompiledScript;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineFactory;
+import javax.script.ScriptException;
+import javax.script.SimpleBindings;
+
+import org.apache.commons.jexl2.JexlContext;
+import org.apache.commons.jexl2.JexlEngine;
+import org.apache.commons.jexl2.Script;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Implements the Jexl ScriptEngine for JSF-223.
+ * <p>
+ * This implementation gives access to both ENGINE_SCOPE and GLOBAL_SCOPE bindings.
+ * When a JEXL script accesses a variable for read or write,
+ * this implementation checks first ENGINE and then GLOBAL scope.
+ * The first one found is used. 
+ * If no variable is found, and the JEXL script is writing to a variable,
+ * it will be stored in the ENGINE scope.
+ * </p>
+ * <p>
+ * The implementation also creates the "JEXL" script object as an instance of the
+ * class {@link JexlScriptObject} for access to utility methods and variables.
+ * </p>
+ * See
+ * <a href="http://java.sun.com/javase/6/docs/api/javax/script/package-summary.html">Java Scripting API</a>
+ * Javadoc.
+ * @since 2.0
+ */
+public class JexlScriptEngine extends AbstractScriptEngine implements Compilable {
+    /** The logger. */
+    private static final Log LOG = LogFactory.getLog(JexlScriptEngine.class);
+
+    /** The shared expression cache size. */
+    private static final int CACHE_SIZE = 512;
+
+    /** Reserved key for context (mandated by JSR-223). */
+    public static final String CONTEXT_KEY = "context";
+
+    /** Reserved key for JexlScriptObject. */
+    public static final String JEXL_OBJECT_KEY = "JEXL";
+
+    /** The JexlScriptObject instance. */
+    private final JexlScriptObject jexlObject;
+
+    /** The factory which created this instance. */
+    private final ScriptEngineFactory parentFactory;
+    
+    /** The JEXL EL engine. */
+    private final JexlEngine jexlEngine;
+    
+    /**
+     * Default constructor.
+     * <p>
+     * Only intended for use when not using a factory.
+     * Sets the factory to {@link JexlScriptEngineFactory}.
+     */
+    public JexlScriptEngine() {
+        this(FactorySingletonHolder.DEFAULT_FACTORY);
+    }
+
+    /**
+     * Implements engine and engine context properties for use by JEXL scripts.
+     * Those properties are allways bound to the default engine scope context.
+     * <p>
+     * The following properties are defined:
+     * <ul>
+     * <li>in - refers to the engine scope reader that defaults to reading System.err</li>
+     * <li>out - refers the engine scope writer that defaults to writing in System.out</li>
+     * <li>err - refers to the engine scope writer that defaults to writing in System.err</li>
+     * <li>logger - the JexlScriptEngine logger</li>
+     * <li>System - the System.class</li>
+     * </ul>
+     * </p>
+     * @since 2.0
+     */
+    public class JexlScriptObject {
+        /**
+         * Gives access to the underlying JEXL engine shared between all ScriptEngine instances.
+         * <p>Although this allows to manipulate various engine flags (lenient, debug, cache...)
+         * for <strong>all</strong> JexlScriptEngine instances, you probably should only do so
+         * if you are in strict control and sole user of the Jexl scripting feature.</p>
+         * @return the shared underlying JEXL engine
+         */
+        public JexlEngine getEngine() {
+            return jexlEngine;
+        }
+
+        /**
+         * Gives access to the engine scope output writer (defaults to System.out).
+         * @return the engine output writer
+         */
+        public PrintWriter getOut() {
+            final Writer out = context.getWriter();
+            if (out instanceof PrintWriter) {
+                return (PrintWriter) out;
+            } else if (out != null) {
+                return new PrintWriter(out, true);
+            } else {
+                return null;
+            }
+        }
+
+        /**
+         * Gives access to the engine scope error writer (defaults to System.err).
+         * @return the engine error writer
+         */
+        public PrintWriter getErr() {
+            final Writer error = context.getErrorWriter();
+            if (error instanceof PrintWriter) {
+                return (PrintWriter) error;
+            } else if (error != null) {
+                return new PrintWriter(error, true);
+            } else {
+                return null;
+            }
+        }
+
+        /**
+         * Gives access to the engine scope input reader (defaults to System.in).
+         * @return the engine input reader
+         */
+        public Reader getIn() {
+            return context.getReader();
+        }
+
+        /**
+         * Gives access to System class.
+         * @return System.class
+         */
+        public Class<System> getSystem() {
+            return System.class;
+        }
+
+        /**
+         * Gives access to the engine logger.
+         * @return the JexlScriptEngine logger
+         */
+        public Log getLogger() {
+            return LOG;
+        }
+    }
+
+
+    /**
+     * Create a scripting engine using the supplied factory.
+     * 
+     * @param factory the factory which created this instance.
+     * @throws NullPointerException if factory is null
+     */
+    public JexlScriptEngine(final ScriptEngineFactory factory) {
+        if (factory == null) {
+            throw new NullPointerException("ScriptEngineFactory must not be null");
+        }
+        parentFactory = factory;
+        jexlEngine = EngineSingletonHolder.DEFAULT_ENGINE;
+        jexlObject = new JexlScriptObject();
+    }
+
+    /** {@inheritDoc} */
+    public Bindings createBindings() {
+        return new SimpleBindings();
+    }
+
+    /** {@inheritDoc} */
+    public Object eval(final Reader reader, final ScriptContext context) throws ScriptException {
+        // This is mandated by JSR-223 (see SCR.5.5.2   Methods)
+        if (reader == null || context == null) {
+            throw new NullPointerException("script and context must be non-null");
+        }
+        return eval(readerToString(reader), context);
+    }
+
+    /** {@inheritDoc} */
+    public Object eval(final String script, final ScriptContext context) throws ScriptException {
+        // This is mandated by JSR-223 (see SCR.5.5.2   Methods)
+        if (script == null || context == null) {
+            throw new NullPointerException("script and context must be non-null");
+        }
+        // This is mandated by JSR-223 (end of section SCR.4.3.4.1.2 - Script Execution)
+        context.setAttribute(CONTEXT_KEY, context, ScriptContext.ENGINE_SCOPE);
+        try {
+            Script jexlScript = jexlEngine.createScript(script);
+            JexlContext ctxt = new JexlContextWrapper(context);
+            return jexlScript.execute(ctxt);
+        } catch (Exception e) {
+            throw new ScriptException(e.toString());
+        }
+    }
+
+    /** {@inheritDoc} */
+    public ScriptEngineFactory getFactory() {
+        return parentFactory;
+    }
+
+    /** {@inheritDoc} */
+    public CompiledScript compile(final String script) throws ScriptException {
+        // This is mandated by JSR-223
+        if (script == null) {
+            throw new NullPointerException("script must be non-null");
+        }
+        try {
+            Script jexlScript = jexlEngine.createScript(script);
+            return new JexlCompiledScript(jexlScript);
+        } catch (Exception e) {
+            throw new ScriptException(e.toString());
+        }
+    }
+
+    /** {@inheritDoc} */
+    public CompiledScript compile(final Reader script) throws ScriptException {
+        // This is mandated by JSR-223
+        if (script == null) {
+            throw new NullPointerException("script must be non-null");
+        }
+        return compile(readerToString(script));
+    }
+
+    /**
+     * Reads a script.
+     * @param script the script reader
+     * @return the script as a string
+     * @throws ScriptException if an exception occurs during read
+     */
+    private String readerToString(final Reader script) throws ScriptException {
+        try {
+           return JexlEngine.readerToString(script);
+        } catch (IOException e) {
+            throw new ScriptException(e);
+        }
+    }
+
+    /**
+     * Holds singleton JexlScriptEngineFactory (IODH). 
+     */
+    private static class FactorySingletonHolder {
+        /** non instantiable. */
+        private FactorySingletonHolder() {}
+        /** The engine factory singleton instance. */
+        private static final JexlScriptEngineFactory DEFAULT_FACTORY = new JexlScriptEngineFactory();
+    }
+
+    /**
+     * Holds singleton JexlScriptEngine (IODH).
+     * <p>A single JEXL engine and Uberspect is shared by all instances of JexlScriptEngine.</p>
+     */
+    private static class EngineSingletonHolder {
+        /** non instantiable. */
+        private EngineSingletonHolder() {}
+        /** The JEXL engine singleton instance. */
+        private static final JexlEngine DEFAULT_ENGINE = new JexlEngine(null, null, null, LOG) {
+            {
+                this.setCache(CACHE_SIZE);
+            }
+        };
+    }
+
+    /**
+     * Wrapper to help convert a JSR-223 ScriptContext into a JexlContext.
+     *
+     * Current implementation only gives access to ENGINE_SCOPE binding.
+     */
+    private final class JexlContextWrapper implements JexlContext {
+        /** The wrapped script context. */
+        private final ScriptContext scriptContext;
+        /**
+         * Creates a context wrapper.
+         * @param theContext the engine context.
+         */
+        private JexlContextWrapper (final ScriptContext theContext){
+            scriptContext = theContext;
+        }
+
+        /** {@inheritDoc} */
+        public Object get(final String name) {
+            final Object o = scriptContext.getAttribute(name);
+            if (JEXL_OBJECT_KEY.equals(name)) {
+                if (o != null) {
+                    LOG.warn("JEXL is a reserved variable name, user defined value is ignored");
+                }
+                return jexlObject;
+            }
+            return o;
+        }
+
+        /** {@inheritDoc} */
+        public void set(final String name, final Object value) {
+            int scope = scriptContext.getAttributesScope(name);
+            if (scope == -1) { // not found, default to engine
+                scope = ScriptContext.ENGINE_SCOPE;
+            }
+            scriptContext.getBindings(scope).put(name , value);
+        }
+
+        /** {@inheritDoc} */
+        public boolean has(final String name) {
+            Bindings bnd = scriptContext.getBindings(ScriptContext.ENGINE_SCOPE);
+            return bnd.containsKey(name);
+        }
+
+    }
+
+    /**
+     * Wrapper to help convert a Jexl Script into a JSR-223 CompiledScript.
+     */
+    private final class JexlCompiledScript extends CompiledScript {
+        /** The underlying Jexl expression instance. */
+        private final Script script;
+
+        /**
+         * Creates an instance.
+         * @param theScript to wrap
+         */
+        private JexlCompiledScript(final Script theScript) {
+            script = theScript;
+        }
+
+        /** {@inheritDoc} */
+        @Override
+        public String toString() {
+            return script.getText();
+        }
+        
+        /** {@inheritDoc} */
+        @Override
+        public Object eval(final ScriptContext context) throws ScriptException {
+            // This is mandated by JSR-223 (end of section SCR.4.3.4.1.2 - Script Execution)
+            context.setAttribute(CONTEXT_KEY, context, ScriptContext.ENGINE_SCOPE);
+            try {
+                JexlContext ctxt = new JexlContextWrapper(context);
+                return script.execute(ctxt);
+            } catch (Exception e) {
+                throw new ScriptException(e.toString());
+            }
+        }
+        
+        /** {@inheritDoc} */
+        @Override
+        public ScriptEngine getEngine() {
+            return JexlScriptEngine.this;
+        }
+    }
+
+
+}
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/scripting/JexlScriptEngineFactory.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/scripting/JexlScriptEngineFactory.java
new file mode 100644
index 0000000..cdf72de
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/scripting/JexlScriptEngineFactory.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2.scripting;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineFactory;
+import org.apache.commons.jexl2.JexlEngine;
+import org.apache.commons.jexl2.parser.StringParser;
+
+/**
+ * Implements the Jexl ScriptEngineFactory for JSF-223.
+ * <p>
+ * Supports the following:<br.>
+ * Language short names: "JEXL", "Jexl", "jexl" <br/>
+ * Extension: "jexl"
+ * </p>
+ * <p>
+ * See
+ * <a href="http://java.sun.com/javase/6/docs/api/javax/script/package-summary.html">Java Scripting API</a>
+ * Javadoc.
+ * @since 2.0
+ */
+public class JexlScriptEngineFactory implements ScriptEngineFactory {
+
+    /** {@inheritDoc} */
+    public String getEngineName() {
+        return "JEXL Engine";
+    }
+
+    /** {@inheritDoc} */
+    public String getEngineVersion() {
+        return "1.0"; // ensure this is updated if function changes are made to this class
+    }
+
+    /** {@inheritDoc} */
+    public String getLanguageName() {
+        return "JEXL";
+    }
+
+    /** {@inheritDoc} */
+    public String getLanguageVersion() {
+        return "2.0"; // TODO this should be derived from the actual version
+    }
+
+    /** {@inheritDoc} */
+    public String getMethodCallSyntax(String obj, String m, String... args) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(obj);
+        sb.append('.');
+        sb.append(m);
+        sb.append('(');
+        boolean needComma = false;
+        for(String arg : args){
+            if (needComma) {
+                sb.append(',');
+            }
+            sb.append(arg);
+            needComma = true;
+        }
+        sb.append(')');
+        return sb.toString();
+    }
+
+    /** {@inheritDoc} */
+    public List<String> getExtensions() {
+        return Collections.unmodifiableList(Arrays.asList("jexl"));
+    }
+
+    /** {@inheritDoc} */
+    public List<String> getMimeTypes() {
+        return Collections.unmodifiableList(Arrays.asList("application/x-jexl"));
+    }
+
+    /** {@inheritDoc} */
+    public List<String> getNames() {
+        return Collections.unmodifiableList(Arrays.asList("JEXL", "Jexl", "jexl"));
+    }
+
+    /** {@inheritDoc} */
+    public String getOutputStatement(String toDisplay) {
+        if (toDisplay == null) {
+            return "JEXL.out.print(null)";
+        } else {
+            return "JEXL.out.print("+StringParser.escapeString(toDisplay)+")";
+        }
+    }
+
+    /** {@inheritDoc} */
+    public Object getParameter(String key) {
+        if (key.equals(ScriptEngine.ENGINE)) {
+            return getEngineName();
+        } else if (key.equals(ScriptEngine.ENGINE_VERSION)) {
+            return getEngineVersion();
+        } else if (key.equals(ScriptEngine.NAME)) {
+            return getNames();
+        } else if (key.equals(ScriptEngine.LANGUAGE)) {
+            return getLanguageName();
+        } else if(key.equals(ScriptEngine.LANGUAGE_VERSION)) {
+            return getLanguageVersion();
+        } else if (key.equals("THREADING")) {
+            /*
+             * To implement multithreading, the scripting engine context (inherited from AbstractScriptEngine)
+             * would need to be made thread-safe; so would the setContext/getContext methods.
+             * It is easier to share the underlying Uberspect and JEXL engine instance, especially
+             * with an expression cache.
+             */
+            return null;
+        }
+        return null;
+    }
+
+    /** {@inheritDoc} */
+    public String getProgram(String... statements) {
+        StringBuilder sb = new StringBuilder();
+        for(String statement : statements){
+            sb.append(JexlEngine.cleanExpression(statement));
+            if (!statement.endsWith(";")){
+                sb.append(';');
+            }
+        }
+        return sb.toString();
+    }
+
+    /** {@inheritDoc} */
+    public ScriptEngine getScriptEngine() {
+        JexlScriptEngine engine = new JexlScriptEngine(this);
+        return engine;
+    }
+
+}
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/scripting/Main.java b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/scripting/Main.java
new file mode 100644
index 0000000..8ce7f4e
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/scripting/Main.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2.scripting;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.InputStreamReader;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptException;
+
+/**
+ * Test application for JexlScriptEngine (JSR-223 implementation).
+ * @since 2.0
+ */
+public class Main {
+
+    /**
+     * Test application for JexlScriptEngine (JSR-223 implementation).
+     * 
+     * If a single argument is present, it is treated as a filename of a JEXL
+     * script to be evaluated. Any exceptions terminate the application.
+     * 
+     * Otherwise, lines are read from standard input and evaluated.
+     * ScriptExceptions are logged, and do not cause the application to exit.
+     * This is done so that interactive testing is easier.
+     * 
+     * @param args (optional) filename to evaluate. Stored in the args variable.
+     * 
+     * @throws Exception if parsing or IO fail
+     */
+    public static void main(String[] args) throws Exception {
+        JexlScriptEngineFactory fac = new JexlScriptEngineFactory();
+        ScriptEngine engine = fac.getScriptEngine();
+        engine.put("args", args);
+        if (args.length == 1){
+            Object value = engine.eval(new FileReader(args[0]));
+            System.out.println("Return value: "+value);
+        } else {
+            BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
+            String line;
+            System.out.print("> ");
+            while(null != (line=console.readLine())){
+                try {
+                    Object value = engine.eval(line);
+                    System.out.println("Return value: "+value);
+                } catch (ScriptException e) {
+                    System.out.println(e.getLocalizedMessage());
+                }
+                System.out.print("> ");
+            }
+        }
+    }
+}
diff --git a/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/scripting/package.html b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/scripting/package.html
new file mode 100644
index 0000000..65966f8
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/java/org/apache/commons/jexl2/scripting/package.html
@@ -0,0 +1,31 @@
+<html>
+    <!--
+       Licensed to the Apache Software Foundation (ASF) under one or more
+       contributor license agreements.  See the NOTICE file distributed with
+       this work for additional information regarding copyright ownership.
+       The ASF licenses this file to You 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.
+    -->
+    <head>
+        <title>Package Documentation for org.apache.commons.jexl2.scripting Package</title>
+    </head>
+    <body bgcolor="white">
+        <p>
+        Contains the JSR-223 Scripting Engine for JEXL script.
+        </p>
+        <p>
+            This internal package is not intended for public usage and there is <b>no</b>
+            guarantee that its public classes or methods will remain as is in subsequent
+            versions.
+        </p>
+    </body>
+</html>
diff --git a/COMMONS_JEXL_2_0/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory b/COMMONS_JEXL_2_0/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory
new file mode 100644
index 0000000..54008cf
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/main/resources/META-INF/services/javax.script.ScriptEngineFactory
@@ -0,0 +1,19 @@
+# 
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You 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.
+#  
+# 
+
+org.apache.commons.jexl2.scripting.JexlScriptEngineFactory
diff --git a/COMMONS_JEXL_2_0/src/site/resources/images/jexl-logo-white.png b/COMMONS_JEXL_2_0/src/site/resources/images/jexl-logo-white.png
new file mode 100644
index 0000000..3ad2943
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/site/resources/images/jexl-logo-white.png
Binary files differ
diff --git a/COMMONS_JEXL_2_0/src/site/resources/images/jexl-logo-white.xcf b/COMMONS_JEXL_2_0/src/site/resources/images/jexl-logo-white.xcf
new file mode 100644
index 0000000..3edf5c2
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/site/resources/images/jexl-logo-white.xcf
Binary files differ
diff --git a/COMMONS_JEXL_2_0/src/site/site.xml b/COMMONS_JEXL_2_0/src/site/site.xml
new file mode 100644
index 0000000..876baf2
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/site/site.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+-->
+<project name="JEXL">
+    <bannerRight>
+        <name>Commons JEXL</name>
+        <src>images/jexl-logo-white.png</src>
+        <href>index.html</href>
+    </bannerRight>
+
+    <body>
+        <menu name="Commons&#xA0;Jexl">
+            <item name="Overview"                href="index.html" />
+            <item name="2.0&#xA0;Javadoc"        href="apidocs/index.html"/>
+            <item name="1.1&#xA0;Javadoc"        href="apidocs-1.1/index.html"/>
+            <item name="Releases and Builds"     href="releases.html"/>
+            <item name="Download"                href="download_jexl.cgi"/>
+            <item name="Reference"               href="reference/index.html"/>
+            <item name="JEXL Wiki"               href="http://wiki.apache.org/commons/JEXL"/>
+        </menu>
+
+        <menu name="Development">
+            <item name="Mailing Lists"           href="mail-lists.html"/>
+            <item name="Issue Tracking"          href="issue-tracking.html"/>
+            <item name="Source Repository"       href="source-repository.html"/>
+            <item name="Building"                href="building.html"/>
+            <item name="Javadoc (SVN latest)"    href="apidocs/index.html"/>
+        </menu>
+
+    </body>
+
+</project>
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/ArithmeticTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/ArithmeticTest.java
new file mode 100644
index 0000000..ec40f55
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/ArithmeticTest.java
@@ -0,0 +1,249 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+import java.util.Map;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import java.util.HashMap;
+import org.apache.commons.jexl2.junit.Asserter;
+
+
+public class ArithmeticTest extends JexlTestCase {
+    private Asserter asserter;
+
+    @Override
+    public void setUp() {
+        asserter = new Asserter(JEXL);
+    }
+
+    public void testBigDecimal() throws Exception {
+        asserter.setVariable("left", new BigDecimal(2));
+        asserter.setVariable("right", new BigDecimal(6));
+        asserter.assertExpression("left + right", new BigDecimal(8));
+        asserter.assertExpression("right - left", new BigDecimal(4));
+        asserter.assertExpression("right * left", new BigDecimal(12));
+        asserter.assertExpression("right / left", new BigDecimal(3));
+        asserter.assertExpression("right % left", new BigDecimal(0));
+    }
+
+    public void testBigInteger() throws Exception {
+        asserter.setVariable("left", new BigInteger("2"));
+        asserter.setVariable("right", new BigInteger("6"));
+        asserter.assertExpression("left + right", new BigInteger("8"));
+        asserter.assertExpression("right - left", new BigInteger("4"));
+        asserter.assertExpression("right * left", new BigInteger("12"));
+        asserter.assertExpression("right / left", new BigInteger("3"));
+        asserter.assertExpression("right % left", new BigInteger("0"));
+    }
+
+    /**
+     * test some simple mathematical calculations
+     */
+    public void testUnaryMinus() throws Exception {
+        asserter.setVariable("aByte", new Byte((byte) 1));
+        asserter.setVariable("aShort", new Short((short) 2));
+        asserter.setVariable("anInteger", new Integer(3));
+        asserter.setVariable("aLong", new Long(4));
+        asserter.setVariable("aFloat", new Float(5.5));
+        asserter.setVariable("aDouble", new Double(6.6));
+        asserter.setVariable("aBigInteger", new BigInteger("7"));
+        asserter.setVariable("aBigDecimal", new BigDecimal("8.8"));
+
+        asserter.assertExpression("-3", new Integer("-3"));
+        asserter.assertExpression("-3.0", new Float("-3.0"));
+        asserter.assertExpression("-aByte", new Byte((byte) -1));
+        asserter.assertExpression("-aShort", new Short((short) -2));
+        asserter.assertExpression("-anInteger", new Integer(-3));
+        asserter.assertExpression("-aLong", new Long(-4));
+        asserter.assertExpression("-aFloat", new Float(-5.5));
+        asserter.assertExpression("-aDouble", new Double(-6.6));
+        asserter.assertExpression("-aBigInteger", new BigInteger("-7"));
+        asserter.assertExpression("-aBigDecimal", new BigDecimal("-8.8"));
+    }
+
+    /**
+     * test some simple mathematical calculations
+     */
+    public void testCalculations() throws Exception {
+
+        asserter.setVariable("foo", new Integer(2));
+
+        asserter.assertExpression("foo + 2", new Integer(4));
+        asserter.assertExpression("3 + 3", new Integer(6));
+        asserter.assertExpression("3 + 3 + foo", new Integer(8));
+        asserter.assertExpression("3 * 3", new Integer(9));
+        asserter.assertExpression("3 * 3 + foo", new Integer(11));
+        asserter.assertExpression("3 * 3 - foo", new Integer(7));
+
+        /*
+         * test parenthesized exprs
+         */
+        asserter.assertExpression("(4 + 3) * 6", new Integer(42));
+        asserter.assertExpression("(8 - 2) * 7", new Integer(42));
+
+        /*
+         * test some floaty stuff
+         */
+        asserter.assertExpression("3 * \"3.0\"", new Double(9));
+        asserter.assertExpression("3 * 3.0", new Double(9));
+
+        /*
+         * test / and %
+         */
+        asserter.assertExpression("6 / 3", new Integer(6 / 3));
+        asserter.assertExpression("6.4 / 3", new Double(6.4 / 3));
+        asserter.assertExpression("0 / 3", new Integer(0 / 3));
+        asserter.assertExpression("3 / 0", new Double(0));
+        asserter.assertExpression("4 % 3", new Integer(1));
+        asserter.assertExpression("4.8 % 3", new Double(4.8 % 3));
+
+        /*
+         * test new null coersion
+         */
+        asserter.setVariable("imanull", null);
+        asserter.assertExpression("imanull + 2", new Integer(2));
+        asserter.assertExpression("imanull + imanull", new Integer(0));
+    }
+
+    public void testCoercions() throws Exception {
+        asserter.assertExpression("1", new Integer(1)); // numerics default to Integer
+//        asserter.assertExpression("5L", new Long(5)); // TODO when implemented
+        
+        asserter.setVariable("I2", new Integer(2));
+        asserter.setVariable("L2", new Long(2));
+        asserter.setVariable("L3", new Long(3));
+        asserter.setVariable("B10", BigInteger.TEN);
+        
+        // Integer & Integer => Integer
+        asserter.assertExpression("I2 + 2", new Integer(4));
+        asserter.assertExpression("I2 * 2", new Integer(4));
+        asserter.assertExpression("I2 - 2", new Integer(0));
+        asserter.assertExpression("I2 / 2", new Integer(1));
+        
+        // Integer & Long => Long
+        asserter.assertExpression("I2 * L2", new Long(4));
+        asserter.assertExpression("I2 / L2", new Long(1));
+
+        // Long & Long => Long
+        asserter.assertExpression("L2 + 3", new Long(5));
+        asserter.assertExpression("L2 + L3", new Long(5));
+        asserter.assertExpression("L2 / L2", new Long(1));
+        asserter.assertExpression("L2 / 2", new Long(1));
+        
+        // BigInteger
+        asserter.assertExpression("B10 / 10", BigInteger.ONE);
+        asserter.assertExpression("B10 / I2", new BigInteger("5"));
+        asserter.assertExpression("B10 / L2", new BigInteger("5"));
+    }
+
+
+    public void testRegexp() throws Exception {
+        asserter.setVariable("str", "abc456");
+        asserter.assertExpression("str =~ '.*456'", Boolean.TRUE);
+        asserter.assertExpression("str !~ 'ABC.*'", Boolean.TRUE);
+        asserter.setVariable("match", "abc.*");
+        asserter.setVariable("nomatch", ".*123");
+        asserter.assertExpression("str =~ match", Boolean.TRUE);
+        asserter.assertExpression("str !~ match", Boolean.FALSE);
+        asserter.assertExpression("str !~ nomatch", Boolean.TRUE);
+        asserter.assertExpression("str =~ nomatch", Boolean.FALSE);
+        asserter.setVariable("match", java.util.regex.Pattern.compile("abc.*"));
+        asserter.setVariable("nomatch", java.util.regex.Pattern.compile(".*123"));
+        asserter.assertExpression("str =~ match", Boolean.TRUE);
+        asserter.assertExpression("str !~ match", Boolean.FALSE);
+        asserter.assertExpression("str !~ nomatch", Boolean.TRUE);
+        asserter.assertExpression("str =~ nomatch", Boolean.FALSE);
+    }
+
+    /**
+     *
+     * if silent, all arith exception return 0.0
+     * if not silent, all arith exception throw
+     * @throws Exception
+     */
+    public void testDivideByZero() throws Exception {
+        Map<String,Object> vars = new HashMap<String,Object>();
+        JexlContext context = new MapContext(vars);
+        vars.put("aByte", new Byte((byte) 1));
+        vars.put("aShort", new Short((short) 2));
+        vars.put("aInteger", new Integer(3));
+        vars.put("aLong", new Long(4));
+        vars.put("aFloat", new Float(5.5));
+        vars.put("aDouble", new Double(6.6));
+        vars.put("aBigInteger", new BigInteger("7"));
+        vars.put("aBigDecimal", new BigDecimal("8.8"));
+
+
+        vars.put("zByte", new Byte((byte) 0));
+        vars.put("zShort", new Short((short) 0));
+        vars.put("zInteger", new Integer(0));
+        vars.put("zLong", new Long(0));
+        vars.put("zFloat", new Float(0));
+        vars.put("zDouble", new Double(0));
+        vars.put("zBigInteger", new BigInteger("0"));
+        vars.put("zBigDecimal", new BigDecimal("0"));
+
+        String[] tnames = {
+            "Byte", "Short", "Integer", "Long",
+            "Float", "Double",
+            "BigInteger", "BigDecimal"
+        };
+        // number of permutations this will generate
+        final int PERMS = tnames.length * tnames.length;
+
+        JexlEngine jexl = new JexlEngine();
+        jexl.setCache(128);
+        jexl.setSilent(false);
+        // for non-silent, silent...
+        for (int s = 0; s < 2; ++s) {
+            jexl.setLenient(s == 0);
+            int zthrow = 0;
+            int zeval = 0;
+            // for vars of all types...
+            for (String vname : tnames) {
+                // for zeros of all types...
+                for (String zname : tnames) {
+                    // divide var by zero
+                    String expr = "a" + vname + " / " + "z" + zname;
+                    try {
+                        Expression zexpr = jexl.createExpression(expr);
+                        Object nan = zexpr.evaluate(context);
+                        // check we have a zero & incremement zero count
+                        if (nan instanceof Number) {
+                            double zero = ((Number) nan).doubleValue();
+                            if (zero == 0.0)
+                                zeval += 1;
+                        }
+                    }
+                    catch (Exception any) {
+                        // increment the exception count
+                        zthrow += 1;
+                    }
+                }
+            }
+            if (!jexl.isLenient())
+                assertTrue("All expressions should have thrown " + zthrow + "/" + PERMS,
+                        zthrow == PERMS);
+            else
+                assertTrue("All expressions should have zeroed " + zeval + "/" + PERMS,
+                        zeval == PERMS);
+        }
+        debuggerCheck(jexl);
+    }
+}
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/ArrayAccessTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/ArrayAccessTest.java
new file mode 100644
index 0000000..68f3a74
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/ArrayAccessTest.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.jexl2.junit.Asserter;
+
+
+/**
+ * Tests for array access operator []
+ * 
+ * @since 2.0
+ */
+public class ArrayAccessTest extends JexlTestCase {
+
+    private Asserter asserter;
+
+    private static final String GET_METHOD_STRING = "GetMethod string";
+    
+    // Needs to be accessible by Foo.class
+    static final String[] GET_METHOD_ARRAY =
+        new String[] { "One", "Two", "Three" };
+
+    // Needs to be accessible by Foo.class
+    static final String[][] GET_METHOD_ARRAY2 =
+        new String[][] { {"One", "Two", "Three"},{"Four", "Five", "Six"} };
+
+    @Override
+    public void setUp() {
+        asserter = new Asserter(JEXL);
+    }
+
+    /**
+     * test simple array access
+     */
+    public void testArrayAccess() throws Exception {
+
+        /*
+         * test List access
+         */
+
+        List<Integer> l = new ArrayList<Integer>();
+        l.add(new Integer(1));
+        l.add(new Integer(2));
+        l.add(new Integer(3));
+
+        asserter.setVariable("list", l);
+
+        asserter.assertExpression("list[1]", new Integer(2));
+        asserter.assertExpression("list[1+1]", new Integer(3));
+        asserter.setVariable("loc", new Integer(1));
+        asserter.assertExpression("list[loc+1]", new Integer(3));
+
+        /*
+         * test array access
+         */
+
+        String[] args = { "hello", "there" };
+        asserter.setVariable("array", args);
+        asserter.assertExpression("array[0]", "hello");
+
+        /*
+         * to think that this was an intentional syntax...
+         */
+        asserter.assertExpression("array.0", "hello");
+
+        /*
+         * test map access
+         */
+        Map<String, String> m = new HashMap<String, String>();
+        m.put("foo", "bar");
+
+        asserter.setVariable("map", m);
+        asserter.setVariable("key", "foo");
+
+        asserter.assertExpression("map[\"foo\"]", "bar");
+        asserter.assertExpression("map[key]", "bar");
+
+        /*
+         * test bean access
+         */
+        asserter.setVariable("foo", new Foo());
+        asserter.assertExpression("foo[\"bar\"]", GET_METHOD_STRING);
+        asserter.assertExpression("foo[\"bar\"] == foo.bar", Boolean.TRUE);
+    }
+
+    /**
+     * test some simple double array lookups
+     */
+    public void testDoubleArrays() throws Exception {
+        Object[][] foo = new Object[2][2];
+        foo[0][0] = "one";
+        foo[0][1] = "two";
+
+        asserter.setVariable("foo", foo);
+
+        asserter.assertExpression("foo[0][1]", "two");
+    }
+
+    public void testArrayProperty() throws Exception {
+        Foo foo = new Foo();
+
+        asserter.setVariable("foo", foo);
+
+        asserter.assertExpression("foo.array[1]", GET_METHOD_ARRAY[1]);
+        asserter.assertExpression("foo.array.1", GET_METHOD_ARRAY[1]);
+        asserter.assertExpression("foo.array2[1][1]", GET_METHOD_ARRAY2[1][1]);
+        // asserter.assertExpression("foo.array2.1.1", GET_METHOD_ARRAY2[1][1]);
+    }
+    
+    // This is JEXL-26
+    public void testArrayAndDottedConflict() throws Exception {
+        Object[] objects = new Object[] {"an", "array", new Long(0)};
+        
+        asserter.setVariable("objects", objects);
+        asserter.setVariable("status", "Enabled");
+        asserter.assertExpression("objects[1].status", null);
+        
+        asserter.setVariable("base.status", "Ok");
+        asserter.assertExpression("base.objects[1].status", null);
+    }
+
+    public void testArrayMethods() throws Exception {
+        Object[] objects = new Object[] {"an", "array", new Long(0)};
+        
+        asserter.setVariable("objects", objects);
+        asserter.assertExpression("objects.get(1)", "array");
+        asserter.assertExpression("objects.size()", new Integer(3));
+        // setting an index returns the old value
+        asserter.assertExpression("objects.set(1, 'dion')", "array");
+        asserter.assertExpression("objects[1]", "dion");
+    }
+}
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/ArrayLiteralTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/ArrayLiteralTest.java
new file mode 100644
index 0000000..3969c29
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/ArrayLiteralTest.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+import java.util.Arrays;
+
+/**
+ * Tests for array literals
+ * @since 2.0
+ */
+public class ArrayLiteralTest extends JexlTestCase {
+
+    public void testLiteralWithStrings() throws Exception {
+        Expression e = JEXL.createExpression( "[ 'foo' , 'bar' ]" );
+        JexlContext jc = new MapContext();
+
+        Object o = e.evaluate( jc );
+        Object[] check = { "foo", "bar" };
+        assertTrue( Arrays.equals(check, (Object[])o) );
+    }
+
+    public void testLiteralWithOneEntry() throws Exception {
+        Expression e = JEXL.createExpression( "[ 'foo' ]" );
+        JexlContext jc = new MapContext();
+
+        Object o = e.evaluate( jc );
+        Object[] check = { "foo" };
+        assertTrue( Arrays.equals(check, (Object[])o) );
+    }
+
+    public void testLiteralWithNumbers() throws Exception {
+        Expression e = JEXL.createExpression( "[ 5.0 , 10 ]" );
+        JexlContext jc = new MapContext();
+
+        Object o = e.evaluate( jc );
+        Object[] check = { new Float(5), new Integer(10) };
+        assertTrue( Arrays.equals(check, (Object[])o) );
+    }
+
+    public void testLiteralWithIntegers() throws Exception {
+        Expression e = JEXL.createExpression( "[ 5 , 10 ]" );
+        JexlContext jc = new MapContext();
+
+        Object o = e.evaluate( jc );
+        int[] check = { 5, 10 };
+        assertTrue( Arrays.equals(check, (int[])o) );
+    }
+
+    public void testSizeOfSimpleArrayLiteral() throws Exception {
+        Expression e = JEXL.createExpression( "size([ 'foo' , 'bar' ])" );
+        JexlContext jc = new MapContext();
+
+        Object o = e.evaluate( jc );
+        assertEquals( new Integer( 2 ), o );
+    }
+
+    public void notestCallingMethodsOnNewMapLiteral() throws Exception {
+        Expression e = JEXL.createExpression( "size({ 'foo' : 'bar' }.values())" );
+        JexlContext jc = new MapContext();
+
+        Object o = e.evaluate( jc );
+        assertEquals( new Integer( 1 ), o );
+    }
+
+    public void testNotEmptySimpleArrayLiteral() throws Exception {
+        Expression e = JEXL.createExpression( "empty([ 'foo' , 'bar' ])" );
+        JexlContext jc = new MapContext();
+
+        Object o = e.evaluate( jc );
+        assertFalse( ( (Boolean) o ).booleanValue() );
+    }
+
+}
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/AssignTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/AssignTest.java
new file mode 100644
index 0000000..c4ee2a3
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/AssignTest.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+/**
+ * Test cases for assignment.
+ * 
+ * @author Dion Gillard
+ * @since 1.1
+ */
+public class AssignTest extends JexlTestCase {
+    private static final JexlEngine ENGINE = new JexlEngine();
+    static {
+        ENGINE.setSilent(false);
+        ENGINE.setLenient(false);
+    }
+    
+    public static class Froboz {
+        int value;
+        public Froboz(int v) {
+            value = v;
+        }
+        public void setValue(int v) {
+            value = v;
+        }
+        public int getValue() {
+            return value;
+        }
+    }
+    
+    public static class Quux {
+        String str;
+        Froboz froboz;
+        public Quux(String str, int fro) {
+            this.str = str;
+            froboz = new Froboz(fro);
+        }
+        
+        public Froboz getFroboz() {
+            return froboz;
+        }
+        
+        public void setFroboz(Froboz froboz) {
+            this.froboz = froboz;
+        }
+        
+        public String getStr() {
+            return str;
+        }
+        
+        public void setStr(String str) {
+            this.str = str;
+        }
+    }
+
+    public AssignTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * Make sure bean assignment works
+     * 
+     * @throws Exception on any error
+     */
+    public void testAntish() throws Exception {
+        Expression assign = ENGINE.createExpression("froboz.value = 10");
+        Expression check = ENGINE.createExpression("froboz.value");
+        JexlContext jc = new MapContext();
+        Object o = assign.evaluate(jc);
+        assertEquals("Result is not 10", new Integer(10), o);
+        o = check.evaluate(jc);
+        assertEquals("Result is not 10", new Integer(10), o);
+    }
+    
+    public void testBeanish() throws Exception {
+        Expression assign = ENGINE.createExpression("froboz.value = 10");
+        Expression check = ENGINE.createExpression("froboz.value");
+        JexlContext jc = new MapContext();
+        Froboz froboz = new Froboz(-169);
+        jc.set("froboz", froboz);
+        Object o = assign.evaluate(jc);
+        assertEquals("Result is not 10", new Integer(10), o);
+        o = check.evaluate(jc);
+        assertEquals("Result is not 10", new Integer(10), o);
+    }
+    
+    public void testAmbiguous() throws Exception {
+        Expression assign = ENGINE.createExpression("froboz.nosuchbean = 10");
+        JexlContext jc = new MapContext();
+        Froboz froboz = new Froboz(-169);
+        jc.set("froboz", froboz);
+        Object o = null;
+        try {
+            o = assign.evaluate(jc);
+        }
+        catch(RuntimeException xrt) {
+            String str = xrt.toString();
+            assertTrue(str.contains("nosuchbean"));
+            return;
+        }
+        finally {
+        assertEquals("Should have failed", null, o);
+        }
+    }
+        
+    public void testArray() throws Exception {
+        Expression assign = ENGINE.createExpression("froboz[\"value\"] = 10");
+        Expression check = ENGINE.createExpression("froboz[\"value\"]");
+        JexlContext jc = new MapContext();
+        Froboz froboz = new Froboz(0);
+        jc.set("froboz", froboz);
+        Object o = assign.evaluate(jc);
+        assertEquals("Result is not 10", new Integer(10), o);
+        o = check.evaluate(jc);
+        assertEquals("Result is not 10", new Integer(10), o);
+    }
+    
+    public void testMore() throws Exception {
+        JexlContext jc = new MapContext();
+        jc.set("quuxClass", Quux.class);
+        Expression create = ENGINE.createExpression("quux = new(quuxClass, 'xuuq', 100)");
+        Expression assign = ENGINE.createExpression("quux.froboz.value = 10");
+        Expression check = ENGINE.createExpression("quux[\"froboz\"].value");
+
+        Quux quux = (Quux) create.evaluate(jc);
+        assertNotNull("quux is null", quux);
+        Object o = assign.evaluate(jc);
+        assertEquals("Result is not 10", new Integer(10), o);
+        o = check.evaluate(jc);
+        assertEquals("Result is not 10", new Integer(10), o);
+    }
+
+    public void testUtil() throws Exception {
+        Quux quux = ENGINE.newInstance(Quux.class, "xuuq", Integer.valueOf(100));
+        ENGINE.setProperty(quux, "froboz.value", Integer.valueOf(100));
+        Object o = ENGINE.getProperty(quux, "froboz.value");
+        assertEquals("Result is not 100", new Integer(100), o);
+        ENGINE.setProperty(quux, "['froboz'].value", Integer.valueOf(1000));
+        o = ENGINE.getProperty(quux, "['froboz']['value']");
+        assertEquals("Result is not 1000", new Integer(1000), o);
+    }
+
+
+}
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/BitwiseOperatorTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/BitwiseOperatorTest.java
new file mode 100644
index 0000000..7248d6e
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/BitwiseOperatorTest.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2;
+import org.apache.commons.jexl2.junit.Asserter;
+
+/**
+ * Tests for the bitwise operators.
+ * @author Dion Gillard
+ * @since 1.1
+ */
+public class BitwiseOperatorTest extends JexlTestCase {
+    private Asserter asserter;
+
+    @Override
+    public void setUp() {
+        asserter = new Asserter(JEXL);
+    }
+
+    /**
+     * Create the named test.
+     * @param name test name
+     */
+    public BitwiseOperatorTest(String name) {
+        super(name);
+    }
+    
+    public void testAndWithTwoNulls() throws Exception {
+        asserter.assertExpression("null & null", new Long(0));
+    }
+
+    public void testAndWithLeftNull() throws Exception {
+        asserter.assertExpression("null & 1", new Long(0));
+    }
+
+    public void testAndWithRightNull() throws Exception {
+        asserter.assertExpression("1 & null", new Long(0));
+    }
+
+    public void testAndSimple() throws Exception {
+        asserter.assertExpression("15 & 3", new Long(15 & 3));
+    }
+
+    public void testAndVariableNumberCoercion() throws Exception {
+        asserter.setVariable("x", new Integer(15));
+        asserter.setVariable("y", new Short((short)7));
+        asserter.assertExpression("x & y", new Long(15 & 7));
+    }
+
+    public void testAndVariableStringCoercion() throws Exception {
+        asserter.setVariable("x", new Integer(15));
+        asserter.setVariable("y", "7");
+        asserter.assertExpression("x & y", new Long(15 & 7));
+    }
+
+    public void testComplementWithNull() throws Exception {
+        asserter.assertExpression("~null", new Long(-1));
+    }
+    
+    public void testComplementSimple() throws Exception {
+        asserter.assertExpression("~128", new Long(-129));
+    }
+
+    public void testComplementVariableNumberCoercion() throws Exception {
+        asserter.setVariable("x", new Integer(15));
+        asserter.assertExpression("~x", new Long(~15));
+    }
+
+    public void testComplementVariableStringCoercion() throws Exception {
+        asserter.setVariable("x", "15");
+        asserter.assertExpression("~x", new Long(~15));
+    }
+
+    public void testOrWithTwoNulls() throws Exception {
+        asserter.assertExpression("null | null", new Long(0));
+    }
+
+    public void testOrWithLeftNull() throws Exception {
+        asserter.assertExpression("null | 1", new Long(1));
+    }
+
+    public void testOrWithRightNull() throws Exception {
+        asserter.assertExpression("1 | null", new Long(1));
+    }
+
+    public void testOrSimple() throws Exception {
+        asserter.assertExpression("12 | 3", new Long(15));
+    }
+
+    public void testOrVariableNumberCoercion() throws Exception {
+        asserter.setVariable("x", new Integer(12));
+        asserter.setVariable("y", new Short((short) 3));
+        asserter.assertExpression("x | y", new Long(15));
+    }
+
+    public void testOrVariableStringCoercion() throws Exception {
+        asserter.setVariable("x", new Integer(12));
+        asserter.setVariable("y", "3");
+        asserter.assertExpression("x | y", new Long(15));
+    }
+
+    public void testXorWithTwoNulls() throws Exception {
+        asserter.assertExpression("null ^ null", new Long(0));
+    }
+
+    public void testXorWithLeftNull() throws Exception {
+        asserter.assertExpression("null ^ 1", new Long(1));
+    }
+
+    public void testXorWithRightNull() throws Exception {
+        asserter.assertExpression("1 ^ null", new Long(1));
+    }
+
+    public void testXorSimple() throws Exception {
+        asserter.assertExpression("1 ^ 3", new Long(1 ^ 3));
+    }
+
+    public void testXorVariableNumberCoercion() throws Exception {
+        asserter.setVariable("x", new Integer(1));
+        asserter.setVariable("y", new Short((short) 3));
+        asserter.assertExpression("x ^ y", new Long(1 ^ 3));
+    }
+
+    public void testXorVariableStringCoercion() throws Exception {
+        asserter.setVariable("x", new Integer(1));
+        asserter.setVariable("y", "3");
+        asserter.assertExpression("x ^ y", new Long(1 ^ 3));
+    }
+
+    public void testParenthesized() throws Exception {
+        asserter.assertExpression("(2 | 1) & 3", Long.valueOf(3L));
+        asserter.assertExpression("(2 & 1) | 3", Long.valueOf(3L));
+        asserter.assertExpression("~(120 | 42)", new Long( ~(120 | 42) ));
+    }
+
+}
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/BlockTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/BlockTest.java
new file mode 100644
index 0000000..c38d21f
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/BlockTest.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+/**
+ * Tests for blocks
+ * @since 1.1
+ */
+public class BlockTest extends JexlTestCase {
+
+    /**
+     * Create the test
+     * 
+     * @param testName name of the test
+     */
+    public BlockTest(String testName) {
+        super(testName);
+    }
+
+    public void testBlockSimple() throws Exception {
+        Expression e = JEXL.createExpression("if (true) { 'hello'; }");
+        JexlContext jc = new MapContext();
+        Object o = e.evaluate(jc);
+        assertEquals("Result is wrong", "hello", o);
+    }
+
+    public void testBlockExecutesAll() throws Exception {
+        Expression e = JEXL.createExpression("if (true) { x = 'Hello'; y = 'World';}");
+        JexlContext jc = new MapContext();
+        Object o = e.evaluate(jc);
+        assertEquals("First result is wrong", "Hello", jc.get("x"));
+        assertEquals("Second result is wrong", "World", jc.get("y"));
+        assertEquals("Block result is wrong", "World", o);
+    }
+
+    public void testEmptyBlock() throws Exception {
+        Expression e = JEXL.createExpression("if (true) { }");
+        JexlContext jc = new MapContext();
+        Object o = e.evaluate(jc);
+        assertNull("Result is wrong", o);
+    }
+
+    public void testBlockLastExecuted01() throws Exception {
+        Expression e = JEXL.createExpression("if (true) { x = 1; } else { x = 2; }");
+        JexlContext jc = new MapContext();
+        Object o = e.evaluate(jc);
+        assertEquals("Block result is wrong", new Integer(1), o);
+    }
+
+    public void testBlockLastExecuted02() throws Exception {
+        Expression e = JEXL.createExpression("if (false) { x = 1; } else { x = 2; }");
+        JexlContext jc = new MapContext();
+        Object o = e.evaluate(jc);
+        assertEquals("Block result is wrong", new Integer(2), o);
+    }
+
+    public void testNestedBlock() throws Exception {
+        Expression e = JEXL.createExpression("if (true) { x = 'hello'; y = 'world';" + " if (true) { x; } y; }");
+        JexlContext jc = new MapContext();
+        Object o = e.evaluate(jc);
+        assertEquals("Block result is wrong", "world", o);
+    }
+}
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/CacheTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/CacheTest.java
new file mode 100644
index 0000000..2c7c7bb
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/CacheTest.java
@@ -0,0 +1,667 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+import java.util.List;
+import java.util.Map;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Verifies cache & tryExecute
+ */
+public class CacheTest extends JexlTestCase {
+    public CacheTest(String testName) {
+        super(testName);
+    }
+    private static final JexlEngine jexl = new JexlEngine();
+
+    static {
+        jexl.setCache(512);
+        jexl.setLenient(false);
+        jexl.setSilent(false);
+    }
+
+    // LOOPS & THREADS
+    private static final int LOOPS = 4096;
+    private static final int NTHREADS = 4;
+    // A pseudo random mix of accessors
+    private static final int[] MIX = {
+        0, 0, 3, 3, 4, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 1, 1, 1, 2, 2, 2,
+        3, 3, 3, 4, 4, 4, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 2, 2, 3, 3, 0
+    };
+
+    @Override
+    protected void tearDown() throws Exception {
+        debuggerCheck(jexl);
+    }
+
+    /**
+     * A set of classes that define different getter/setter methods for the same properties.
+     * The goal is to verify that the cached JexlPropertyGet / JexlPropertySet in the AST Nodes are indeed
+     * volatile and do not generate errors even when multiple threads concurently hammer them.
+     */
+    public static class Cached {
+        public String compute(String arg) {
+            if (arg == null) {
+                arg = "na";
+            }
+            return getClass().getSimpleName() + "@s#" + arg;
+        }
+
+        public String compute(String arg0, String arg1) {
+            if (arg0 == null) {
+                arg0 = "na";
+            }
+            if (arg1 == null) {
+                arg1 = "na";
+            }
+            return getClass().getSimpleName() + "@s#" + arg0 + ",s#" + arg1;
+        }
+
+        public String compute(Integer arg) {
+            return getClass().getSimpleName() + "@i#" + arg;
+        }
+
+        public String compute(float arg) {
+            return getClass().getSimpleName() + "@f#" + arg;
+        }
+
+        public String compute(int arg0, int arg1) {
+            return getClass().getSimpleName() + "@i#" + arg0 + ",i#" + arg1;
+        }
+
+        public String ambiguous(Integer arg0, int arg1) {
+            return getClass().getSimpleName() + "!i#" + arg0 + ",i#" + arg1;
+        }
+
+        public String ambiguous(int arg0, Integer arg1) {
+            return getClass().getSimpleName() + "!i#" + arg0 + ",i#" + arg1;
+        }
+
+        public static String COMPUTE(String arg) {
+            if (arg == null) {
+                arg = "na";
+            }
+            return "CACHED@s#" + arg;
+        }
+
+        public static String COMPUTE(String arg0, String arg1) {
+            if (arg0 == null) {
+                arg0 = "na";
+            }
+            if (arg1 == null) {
+                arg1 = "na";
+            }
+            return "CACHED@s#" + arg0 + ",s#" + arg1;
+        }
+
+        public static String COMPUTE(int arg) {
+            return "CACHED@i#" + arg;
+        }
+
+        public static String COMPUTE(int arg0, int arg1) {
+            return "CACHED@i#" + arg0 + ",i#" + arg1;
+        }
+    }
+
+    public static class Cached0 extends Cached {
+        protected String value = "Cached0:new";
+        protected Boolean flag = Boolean.FALSE;
+
+        public Cached0() {
+        }
+
+        public String getValue() {
+            return value;
+        }
+
+        public void setValue(String arg) {
+            if (arg == null) {
+                arg = "na";
+            }
+            value = "Cached0:" + arg;
+        }
+
+        public void setFlag(boolean b) {
+            flag = Boolean.valueOf(b);
+        }
+
+        public boolean isFlag() {
+            return flag.booleanValue();
+        }
+    }
+
+    public static class Cached1 extends Cached0 {
+        @Override
+        public void setValue(String arg) {
+            if (arg == null) {
+                arg = "na";
+            }
+            value = "Cached1:" + arg;
+        }
+    }
+
+    public static class Cached2 extends Cached {
+        boolean flag = false;
+        protected String value;
+
+        public Cached2() {
+            value = "Cached2:new";
+        }
+
+        public Object get(String prop) {
+            if ("value".equals(prop)) {
+                return value;
+            } else if ("flag".equals(prop)) {
+                return Boolean.valueOf(flag);
+            }
+            throw new RuntimeException("no such property");
+        }
+
+        public void set(String p, Object v) {
+            if (v == null) {
+                v = "na";
+            }
+            if ("value".equals(p)) {
+                value = getClass().getSimpleName() + ":" + v;
+            } else if ("flag".equals(p)) {
+                flag = Boolean.parseBoolean(v.toString());
+            } else {
+                throw new RuntimeException("no such property");
+            }
+        }
+    }
+
+    public static class Cached3 extends java.util.TreeMap<String, Object> {
+        private static final long serialVersionUID = 1L;
+        boolean flag = false;
+
+        public Cached3() {
+            put("value", "Cached3:new");
+            put("flag", "false");
+        }
+
+        @Override
+        public Object get(Object key) {
+            return super.get(key.toString());
+        }
+
+        @Override
+        public Object put(String key, Object arg) {
+            if (arg == null) {
+                arg = "na";
+            }
+            arg = "Cached3:" + arg;
+            return super.put(key, arg);
+        }
+
+        public void setflag(boolean b) {
+            flag = b;
+        }
+
+        public boolean isflag() {
+            return flag;
+        }
+    }
+
+    public static class Cached4 extends java.util.ArrayList<String> {
+        private static final long serialVersionUID = 1L;
+
+        public Cached4() {
+            super.add("Cached4:new");
+            super.add("false");
+        }
+
+        public String getValue() {
+            return super.get(0);
+        }
+
+        public void setValue(String arg) {
+            if (arg == null) {
+                arg = "na";
+            }
+            super.set(0, "Cached4:" + arg);
+        }
+
+        public void setflag(Boolean b) {
+            super.set(1, b.toString());
+        }
+
+        public boolean isflag() {
+            return Boolean.parseBoolean(super.get(1));
+        }
+    }
+
+    /**
+     * A helper class to pass arguments in tests (instances of getter/setter exercising classes).
+     */
+    static class TestCacheArguments {
+        Cached0 c0 = new Cached0();
+        Cached1 c1 = new Cached1();
+        Cached2 c2 = new Cached2();
+        Cached3 c3 = new Cached3();
+        Cached4 c4 = new Cached4();
+        Object[] ca = {
+            c0, c1, c2, c3, c4
+        };
+        Object[] value = null;
+    }
+
+    /**
+     * Run same test function in NTHREADS in parallel.
+     * @param ctask the task / test
+     * @param loops number of loops to perform
+     * @param cache whether jexl cache is used or not
+     * @throws Exception if anything goes wrong
+     */
+    void runThreaded(Class<? extends Task> ctask, int loops, boolean cache) throws Exception {
+        if (loops == 0) {
+            loops = MIX.length;
+        }
+        if (cache) {
+            jexl.setCache(32);
+        } else {
+            jexl.setCache(0);
+        }
+        java.util.concurrent.ExecutorService execs = java.util.concurrent.Executors.newFixedThreadPool(NTHREADS);
+        List<Callable<Integer>> tasks = new ArrayList<Callable<Integer>>(NTHREADS);
+        for(int t = 0; t < NTHREADS; ++t) {
+            tasks.add(jexl.newInstance(ctask, loops));
+        }
+        // let's not wait for more than a minute
+        List<Future<Integer>> futures = execs.invokeAll(tasks, 60, TimeUnit.SECONDS);
+        // check that all returned loops
+        for(Future<Integer> future : futures) {
+            assertEquals(Integer.valueOf(loops), future.get());
+        }
+    }
+
+    /**
+     * The base class for MT tests.
+     */
+    public abstract static class Task implements Callable<Integer> {
+        final TestCacheArguments args = new TestCacheArguments();
+        final int loops;
+        final Map<String, Object> vars = new HashMap<String, Object>();
+        final JexlContext jc = new MapContext(vars);
+
+        Task(int loops) {
+            this.loops = loops;
+        }
+
+        public abstract Integer call() throws Exception;
+
+        /**
+         * The actual test function; assigns and checks.
+         * <p>The expression will be evaluated against different classes in parallel.
+         * This verifies that neither the volatile cache in the AST nor the expression cache in the JEXL engine
+         * induce errors.</p>
+         * <p>
+         * Using it as a micro benchmark, it shows creating expression as the dominating cost; the expression
+         * cache takes care of this.
+         * By moving the expression creations out of the main loop, it also shows that the volatile cache speeds
+         * things up around 2x.
+         * </p>
+         * @param value the argument value to control
+         * @return the number of loops performed
+         */
+        public Integer runAssign(Object value) {
+            args.value = new Object[]{value};
+            Object result;
+
+            Expression cacheGetValue = jexl.createExpression("cache.value");
+            Expression cacheSetValue = jexl.createExpression("cache.value = value");
+            for (int l = 0; l < loops; ++l) {
+                int px = (int) Thread.currentThread().getId();
+                int mix = MIX[(l + px) % MIX.length];
+
+                vars.put("cache", args.ca[mix]);
+                vars.put("value", args.value[0]);
+                result = cacheSetValue.evaluate(jc);
+                if (args.value[0] == null) {
+                    assertNull(cacheSetValue.toString(), result);
+                } else {
+                    assertEquals(cacheSetValue.toString(), args.value[0], result);
+                }
+
+                result = cacheGetValue.evaluate(jc);
+                if (args.value[0] == null) {
+                    assertEquals(cacheGetValue.toString(), "Cached" + mix + ":na", result);
+                } else {
+                    assertEquals(cacheGetValue.toString(), "Cached" + mix + ":" + args.value[0], result);
+                }
+
+            }
+
+            return Integer.valueOf(loops);
+        }
+    }
+
+    /**
+     * A task to check assignment.
+     */
+    public static class AssignTask extends Task {
+        public AssignTask(int loops) {
+            super(loops);
+        }
+        public Integer call() throws Exception {
+            return runAssign("foo");
+        }
+    }
+
+    /**
+     * A task to check null assignment.
+     */
+    public static class AssignNullTask extends Task {
+        public AssignNullTask(int loops) {
+            super(loops);
+        }
+        public Integer call() throws Exception {
+            return runAssign(null);
+        }
+    }
+
+    /**
+     * A task to check boolean assignment.
+     */
+    public static class AssignBooleanTask extends Task {
+        public AssignBooleanTask(int loops) {
+            super(loops);
+        }
+        public Integer call() throws Exception {
+            return runAssignBoolean(Boolean.TRUE);
+        }
+
+        /** The actual test function. */
+        private Integer runAssignBoolean(Boolean value) {
+            args.value = new Object[]{value};
+            Expression cacheGetValue = jexl.createExpression("cache.flag");
+            Expression cacheSetValue = jexl.createExpression("cache.flag = value");
+            Object result;
+
+            for (int l = 0; l < loops; ++l) {
+                int px = (int) Thread.currentThread().getId();
+                int mix = MIX[(l + px) % MIX.length];
+
+                vars.put("cache", args.ca[mix]);
+                vars.put("value", args.value[0]);
+                result = cacheSetValue.evaluate(jc);
+                assertEquals(cacheSetValue.toString(), args.value[0], result);
+
+                result = cacheGetValue.evaluate(jc);
+                assertEquals(cacheGetValue.toString(), args.value[0], result);
+
+            }
+
+            return Integer.valueOf(loops);
+        }
+    }
+
+    /**
+     * A task to check list assignment.
+     */
+    public static class AssignListTask extends Task {
+        public AssignListTask(int loops) {
+            super(loops);
+        }
+
+        public Integer call() throws Exception {
+            return runAssignList();
+        }
+        /** The actual test function. */
+        private Integer runAssignList() {
+            args.value = new Object[]{"foo"};
+            java.util.ArrayList<String> c1 = new java.util.ArrayList<String>(2);
+            c1.add("foo");
+            c1.add("bar");
+            args.ca = new Object[]{
+                        new String[]{"one", "two"},
+                        c1
+                    };
+
+            Expression cacheGetValue = jexl.createExpression("cache.0");
+            Expression cacheSetValue = jexl.createExpression("cache[0] = value");
+            Object result;
+
+            for (int l = 0; l < loops; ++l) {
+                int px = (int) Thread.currentThread().getId();
+                int mix = MIX[(l + px) % MIX.length] % args.ca.length;
+
+                vars.put("cache", args.ca[mix]);
+                vars.put("value", args.value[0]);
+                result = cacheSetValue.evaluate(jc);
+                assertEquals(cacheSetValue.toString(), args.value[0], result);
+
+                result = cacheGetValue.evaluate(jc);
+                assertEquals(cacheGetValue.toString(), args.value[0], result);
+            }
+
+            return Integer.valueOf(loops);
+        }
+    }
+
+
+    public void testNullAssignNoCache() throws Exception {
+        runThreaded(AssignNullTask.class, LOOPS, false);
+    }
+
+    public void testNullAssignCache() throws Exception {
+        runThreaded(AssignNullTask.class, LOOPS, true);
+    }
+
+    public void testAssignNoCache() throws Exception {
+        runThreaded(AssignTask.class, LOOPS, false);
+    }
+
+    public void testAssignCache() throws Exception {
+        runThreaded(AssignTask.class, LOOPS, true);
+    }
+
+    public void testAssignBooleanNoCache() throws Exception {
+        runThreaded(AssignBooleanTask.class, LOOPS, false);
+    }
+
+    public void testAssignBooleanCache() throws Exception {
+        runThreaded(AssignBooleanTask.class, LOOPS, true);
+    }
+
+    public void testAssignListNoCache() throws Exception {
+        runThreaded(AssignListTask.class, LOOPS, false);
+    }
+
+    public void testAssignListCache() throws Exception {
+        runThreaded(AssignListTask.class, LOOPS, true);
+    }
+
+    /**
+     * A task to check method calls.
+     */
+    public static class ComputeTask extends Task {
+        public ComputeTask(int loops) {
+            super(loops);
+        }
+
+        public Integer call() throws Exception {
+            args.ca = new Object[]{args.c0, args.c1, args.c2};
+            args.value = new Object[]{new Integer(2), "quux"};
+            //jexl.setDebug(true);
+            Expression compute2 = jexl.createExpression("cache.compute(a0, a1)");
+            Expression compute1 = jexl.createExpression("cache.compute(a0)");
+            Expression compute1null = jexl.createExpression("cache.compute(a0)");
+            Expression ambiguous = jexl.createExpression("cache.ambiguous(a0, a1)");
+            //jexl.setDebug(false);
+
+            Object result = null;
+            String expected = null;
+            for (int l = 0; l < loops; ++l) {
+                int mix = MIX[l % MIX.length] % args.ca.length;
+                Object value = args.value[l % args.value.length];
+
+                vars.put("cache", args.ca[mix]);
+                if (value instanceof String) {
+                    vars.put("a0", "S0");
+                    vars.put("a1", "S1");
+                    expected = "Cached" + mix + "@s#S0,s#S1";
+                } else if (value instanceof Integer) {
+                    vars.put("a0", Integer.valueOf(7));
+                    vars.put("a1", Integer.valueOf(9));
+                    expected = "Cached" + mix + "@i#7,i#9";
+                } else {
+                    fail("unexpected value type");
+                }
+                result = compute2.evaluate(jc);
+                assertEquals(compute2.toString(), expected, result);
+
+                if (value instanceof Integer) {
+                    try {
+                        vars.put("a0", Short.valueOf((short) 17));
+                        vars.put("a1", Short.valueOf((short) 19));
+                        result = ambiguous.evaluate(jc);
+                        fail("should have thrown an exception");
+                    } catch (JexlException xany) {
+                        // throws due to ambiguous exception
+                    }
+                }
+
+                if (value instanceof String) {
+                    vars.put("a0", "X0");
+                    expected = "Cached" + mix + "@s#X0";
+                } else if (value instanceof Integer) {
+                    vars.put("a0", Integer.valueOf(5));
+                    expected = "Cached" + mix + "@i#5";
+                } else {
+                    fail("unexpected value type");
+                }
+                result = compute1.evaluate(jc);
+                assertEquals(compute1.toString(), expected, result);
+
+                try {
+                    vars.put("a0", null);
+                    result = compute1null.evaluate(jc);
+                    fail("should have thrown an exception");
+                } catch (JexlException xany) {
+                    // throws due to ambiguous exception
+                    String sany = xany.getMessage();
+                    String tname = getClass().getName();
+                    if (!sany.startsWith(tname)) {
+                        fail("debug mode should carry caller information, "
+                                + sany + ", "
+                                + tname);
+                    }
+                }
+            }
+            return Integer.valueOf(loops);
+        }
+    }
+
+    public void testComputeNoCache() throws Exception {
+        try {
+            jexl.setDebug(true);
+            runThreaded(ComputeTask.class, LOOPS, false);
+        } finally {
+            jexl.setDebug(false);
+        }
+    }
+
+    public void testComputeCache() throws Exception {
+        try {
+            jexl.setDebug(true);
+            runThreaded(ComputeTask.class, LOOPS, true);
+        } finally {
+            jexl.setDebug(false);
+        }
+    }
+
+    /**
+     * The remaining tests exercise the namespaced functions; not MT.
+     * @param x
+     * @param loops
+     * @param cache
+     * @throws Exception
+     */
+    void doCOMPUTE(TestCacheArguments x, int loops, boolean cache) throws Exception {
+        if (loops == 0) {
+            loops = MIX.length;
+        }
+        if (cache) {
+            jexl.setCache(32);
+        } else {
+            jexl.setCache(0);
+        }
+        Map<String, Object> vars = new HashMap<String, Object>();
+        JexlContext jc = new MapContext(vars);
+        java.util.Map<String, Object> funcs = new java.util.HashMap<String, Object>();
+        jexl.setFunctions(funcs);
+        Expression compute2 = jexl.createExpression("cached:COMPUTE(a0, a1)");
+        Expression compute1 = jexl.createExpression("cached:COMPUTE(a0)");
+        Object result = null;
+        String expected = null;
+        for (int l = 0; l < loops; ++l) {
+            int mix = MIX[l % MIX.length] % x.ca.length;
+            Object value = x.value[l % x.value.length];
+
+            funcs.put("cached", x.ca[mix]);
+            if (value instanceof String) {
+                vars.put("a0", "S0");
+                vars.put("a1", "S1");
+                expected = "CACHED@s#S0,s#S1";
+            } else if (value instanceof Integer) {
+                vars.put("a0", Integer.valueOf(7));
+                vars.put("a1", Integer.valueOf(9));
+                expected = "CACHED@i#7,i#9";
+            } else {
+                fail("unexpected value type");
+            }
+            result = compute2.evaluate(jc);
+            assertEquals(compute2.toString(), expected, result);
+
+            if (value instanceof String) {
+                vars.put("a0", "X0");
+                expected = "CACHED@s#X0";
+            } else if (value instanceof Integer) {
+                vars.put("a0", Integer.valueOf(5));
+                expected = "CACHED@i#5";
+            } else {
+                fail("unexpected value type");
+            }
+            result = compute1.evaluate(jc);
+            assertEquals(compute1.toString(), expected, result);
+        }
+    }
+
+    public void testCOMPUTENoCache() throws Exception {
+        TestCacheArguments args = new TestCacheArguments();
+        args.ca = new Object[]{
+                    Cached.class, Cached1.class, Cached2.class
+                };
+        args.value = new Object[]{new Integer(2), "quux"};
+        doCOMPUTE(args, LOOPS, false);
+    }
+
+    public void testCOMPUTECache() throws Exception {
+        TestCacheArguments args = new TestCacheArguments();
+        args.ca = new Object[]{
+                    Cached.class, Cached1.class, Cached2.class
+                };
+        args.value = new Object[]{new Integer(2), "quux"};
+        doCOMPUTE(args, LOOPS, true);
+    }
+}
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/ClassCreator.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/ClassCreator.java
new file mode 100644
index 0000000..20959ad
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/ClassCreator.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+/**
+ * Helper class to test GC / reference interactions.
+ * Dynamically creates a class by compiling generated source Java code and
+ * load it through a dedicated class loader.
+ */
+public class ClassCreator {
+    private final JexlEngine jexl;
+    private final File base;
+    private File packageDir = null;
+    private int seed = 0;
+    private String className = null;
+    private String sourceName = null;
+    private ClassLoader loader = null;
+    public static final boolean canRun = comSunToolsJavacMain();
+    /**
+     * Check if we can invoke Sun's java compiler.
+     * @return true if it is possible, false otherwise
+     */
+    private static boolean comSunToolsJavacMain() {
+        try {
+            Class<?> javac = ClassCreatorTest.class.getClassLoader().loadClass("com.sun.tools.javac.Main");
+            return javac != null;
+        } catch (Exception xany) {
+            return false;
+        }
+    }
+
+    public ClassCreator(JexlEngine theJexl, File theBase) throws Exception {
+        jexl = theJexl;
+        base = theBase;
+    }
+
+
+    public void clear() {
+        seed = 0;
+        packageDir = null;
+        className = null;
+        sourceName = null;
+        packageDir = null;
+        loader = null;
+    }
+
+    public void setSeed(int s) {
+        seed = s;
+        className = "foo" + s;
+        sourceName = className + ".java";
+        packageDir = new File(base, seed + "/org/apache/commons/jexl2/generated");
+        packageDir.mkdirs();
+        loader = null;
+    }
+
+    public String getClassName() {
+        return "org.apache.commons.jexl2.generated." + className;
+    }
+
+    public Class<?> getClassInstance() throws Exception {
+        return getClassLoader().loadClass("org.apache.commons.jexl2.generated." + className);
+    }
+
+    public ClassLoader getClassLoader() throws Exception {
+        if (loader == null) {
+            URL classpath = (new File(base, Integer.toString(seed))).toURI().toURL();
+            loader = new URLClassLoader(new URL[]{classpath}, null);
+        }
+        return loader;
+    }
+
+    public Class<?> createClass() throws Exception {
+        // generate, compile & validate
+        generate();
+        Class<?> clazz = compile();
+        if (clazz == null) {
+            throw new Exception("failed to compile foo" + seed);
+        }
+        Object v = validate(clazz);
+        if (v instanceof Integer && ((Integer) v).intValue() == seed) {
+            return clazz;
+        }
+        throw new Exception("failed to validate foo" + seed);
+    }
+
+    void generate() throws Exception {
+        FileWriter aWriter = new FileWriter(new File(packageDir, sourceName), false);
+        aWriter.write("package org.apache.commons.jexl2.generated;");
+        aWriter.write("public class " + className + "{\n");
+        aWriter.write("private int value =");
+        aWriter.write(Integer.toString(seed));
+        aWriter.write(";\n");
+        aWriter.write(" public void setValue(int v) {");
+        aWriter.write(" value = v;");
+        aWriter.write(" }\n");
+        aWriter.write(" public int getValue() {");
+        aWriter.write(" return value;");
+        aWriter.write(" }\n");
+        aWriter.write(" }\n");
+        aWriter.flush();
+        aWriter.close();
+    }
+
+    Class<?> compile() throws Exception {
+        String source = packageDir.getPath() + "/" + sourceName;
+        Class<?> javac = getClassLoader().loadClass("com.sun.tools.javac.Main");
+        if (javac == null) {
+            return null;
+        }
+        Integer r = (Integer) jexl.invokeMethod(javac, "compile", source);
+        if (r.intValue() >= 0) {
+            return getClassLoader().loadClass("org.apache.commons.jexl2.generated." + className);
+        }
+        return null;
+    }
+
+    Object validate(Class<?> clazz) throws Exception {
+        Class<?> params[] = {};
+        Object paramsObj[] = {};
+        Object iClass = clazz.newInstance();
+        Method thisMethod = clazz.getDeclaredMethod("getValue", params);
+        return thisMethod.invoke(iClass, paramsObj);
+    }
+}
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/ClassCreatorTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/ClassCreatorTest.java
new file mode 100644
index 0000000..e786b1c
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/ClassCreatorTest.java
@@ -0,0 +1,191 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+import java.io.File;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Basic check on automated class creation
+ */
+public class ClassCreatorTest extends JexlTestCase {
+    static final Log logger = LogFactory.getLog(JexlTestCase.class);
+    static final int LOOPS = 8;
+    private File base = null;
+    private JexlEngine jexl = null;
+
+    @Override
+    public void setUp() throws Exception {
+        base = new File(System.getProperty("java.io.tmpdir") + File.pathSeparator + "jexl" + System.currentTimeMillis());
+        jexl = new JexlEngine();
+        jexl.setCache(512);
+
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        deleteDirectory(base);
+    }
+
+    private void deleteDirectory(File dir) {
+        if (dir.isDirectory()) {
+            for (File file : dir.listFiles()) {
+                if (file.isFile()) {
+                    file.delete();
+                }
+            }
+        }
+        dir.delete();
+    }
+
+    // A space hog class
+    static final int MEGA = 1024 * 1024;
+    public class BigObject {
+        @SuppressWarnings("unused")
+        private final byte[] space = new byte[MEGA];
+        private final int id;
+
+        public BigObject(int id) {
+            this.id = id;
+        }
+
+        public int getId() {
+            return id;
+        }
+    }
+
+    // A soft reference on class
+    static final class ClassReference extends WeakReference<Class<?>> {
+        ClassReference(Class<?> clazz, ReferenceQueue<Object> queue) {
+            super(clazz, queue);
+        }
+    }
+    // A weak reference on instance
+    static final class InstanceReference extends SoftReference<Object> {
+        InstanceReference(Object obj, ReferenceQueue<Object> queue) {
+            super(obj, queue);
+        }
+    }
+
+    public void testOne() throws Exception {
+        // abort test if class creator can not run
+        if (!ClassCreator.canRun) {
+            return;
+        }
+        ClassCreator cctor = new ClassCreator(jexl, base);
+        cctor.setSeed(1);
+        Class<?> foo1 = cctor.createClass();
+        assertEquals("foo1", foo1.getSimpleName());
+        cctor.clear();
+    }
+
+    public void testMany() throws Exception {
+        // abort test if class creator can not run
+        if (!ClassCreator.canRun) {
+            return;
+        }
+        int pass = 0;
+        int gced = -1;
+        ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
+        List<Reference<?>> stuff = new ArrayList<Reference<?>>();
+        // keeping a reference on methods prevent classes from being GCed
+//        List<Object> mm = new ArrayList<Object>();
+        Expression expr = jexl.createExpression("foo.value");
+        Expression newx = jexl.createExpression("foo = new(clazz)");
+        JexlContext context = new MapContext();
+
+        ClassCreator cctor = new ClassCreator(jexl, base);
+        for (int i = 0; i < LOOPS && gced < 0; ++i) {
+            cctor.setSeed(i);
+            Class<?> clazz;
+            if (pass == 0) {
+                clazz = cctor.createClass();
+            } else {
+                clazz = cctor.getClassInstance();
+                if (clazz == null) {
+                    assertEquals(i, gced);
+                    break;
+                }
+            }
+            // this code verifies the assumption that holding a strong reference to a method prevents
+            // its owning class from being GCed
+//          Method m = clazz.getDeclaredMethod("getValue", new Class<?>[0]);
+//          mm.add(m);
+            // we should not be able to create foox since it is unknown to the Jexl classloader
+            context.set("clazz", cctor.getClassName());
+            context.set("foo", null);
+            Object z = newx.evaluate(context);
+            assertNull(z);
+            // check with the class itself
+            context.set("clazz", clazz);
+            z = newx.evaluate(context);
+            assertNotNull(clazz + ": class " + i + " could not be instantiated on pass " + pass, z);
+            assertEquals(new Integer(i), expr.evaluate(context));
+            // with the proper class loader, attempt to create an instance from the class name
+            jexl.setClassLoader(cctor.getClassLoader());
+            z = newx.evaluate(context);
+            assertTrue(z.getClass().equals(clazz));
+            assertEquals(new Integer(i), expr.evaluate(context));
+            cctor.clear();
+            jexl.setClassLoader(null);
+
+            // on pass 0, attempt to force GC to run and collect generated classes
+            if (pass == 0) {
+                // add a weak reference on the class
+                stuff.add(new ClassReference(clazz, queue));
+                // add a soft reference on an instance
+                stuff.add(new InstanceReference(clazz.newInstance(), queue));
+
+                // attempt to force GC:
+                // while we still have a MB free, create & store big objects
+                for (int b = 0; b < 64 && Runtime.getRuntime().freeMemory() > MEGA; ++b) {
+                    BigObject big = new BigObject(b);
+                    stuff.add(new InstanceReference(big, queue));
+                }
+                // hint it...
+                System.gc();
+                // let's see if some weak refs got collected
+                boolean qr = false;
+                while (queue.poll() != null) {
+                    Reference<?> ref = queue.remove(1);
+                    if (ref instanceof ClassReference) {
+                        gced = i;
+                        qr = true;
+                    }
+                }
+                if (qr) {
+                    //logger.warn("may have GCed class around " + i);
+                    pass = 1;
+                    i = 0;
+                }
+            }
+        }
+
+        if (gced < 0) {
+            logger.warn("unable to force GC");
+            //assertTrue(gced > 0);
+        }
+    }
+
+}
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/Foo.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/Foo.java
new file mode 100644
index 0000000..158c708
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/Foo.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A simple bean used for testing purposes
+ * 
+ * @since 1.0
+ * @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
+ * @version $Revision$
+ */
+public class Foo {
+    
+    private boolean beenModified = false;
+    private String property1 = "some value";
+    public Foo() {}
+    public class Cheezy {
+        public Iterator<String> iterator() {
+            return getCheeseList().iterator();
+        }
+    }
+    
+    public String bar()
+    {
+        return JexlTest.METHOD_STRING;
+    }
+
+    public String getBar()
+    {
+        return JexlTest.GET_METHOD_STRING;
+    }
+
+    public Foo getInnerFoo()
+    {
+        return new Foo();
+    }
+
+    public String get(String arg)
+    {
+        return "Repeat : " + arg;
+    }
+
+    public String convertBoolean(boolean b)
+    {
+        return "Boolean : " + b;
+    }
+
+    public int getCount() {
+        return 5;
+    }
+
+    public List<String> getCheeseList()
+    {
+        ArrayList<String> answer = new ArrayList<String>();
+        answer.add("cheddar");
+        answer.add("edam");
+        answer.add("brie");
+        return answer;
+    }
+
+    public Cheezy getCheezy()
+    {
+        return new Cheezy();
+    }
+
+    public String[] getArray()
+    {
+        return ArrayAccessTest.GET_METHOD_ARRAY;
+    }
+
+    public String[][] getArray2()
+    {
+        return ArrayAccessTest.GET_METHOD_ARRAY2;
+    }
+
+    public boolean isSimple()
+    {
+        return true;
+    }
+
+    public int square(int value)
+    {
+        return value * value;
+    }
+
+    public boolean getTrueAndModify()
+    {
+        beenModified = true;
+        return true;
+    }
+
+    public boolean getModified()
+    {
+        return beenModified;
+    }
+
+
+    public int getSize()
+    {
+        return 22;
+    }
+    
+    public String getProperty1() {
+        return property1;
+    }
+
+    public void setProperty1(String newValue) {
+        property1 = newValue;
+    }
+}
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/ForEachTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/ForEachTest.java
new file mode 100644
index 0000000..42b28ef
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/ForEachTest.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+
+/**
+ * Tests for the foreach statement
+ * @author Dion Gillard
+ * @since 1.1
+ */
+public class ForEachTest extends JexlTestCase {
+
+    /** create a named test */
+    public ForEachTest(String name) {
+        super(name);
+    }
+
+    public void testForEachWithEmptyStatement() throws Exception {
+        Expression e = JEXL.createExpression("for(item : list) ;");
+        JexlContext jc = new MapContext();
+
+        Object o = e.evaluate(jc);
+        assertNull("Result is not null", o);
+    }
+
+    public void testForEachWithEmptyList() throws Exception {
+        Expression e = JEXL.createExpression("for(item : list) 1+1");
+        JexlContext jc = new MapContext();
+
+        Object o = e.evaluate(jc);
+        assertNull("Result is not null", o);
+    }
+
+    public void testForEachWithArray() throws Exception {
+        Expression e = JEXL.createExpression("for(item : list) item");
+        JexlContext jc = new MapContext();
+        jc.set("list", new Object[] {"Hello", "World"});
+        Object o = e.evaluate(jc);
+        assertEquals("Result is not last evaluated expression", "World", o);
+    }
+
+    public void testForEachWithCollection() throws Exception {
+        Expression e = JEXL.createExpression("for(item : list) item");
+        JexlContext jc = new MapContext();
+        jc.set("list", Arrays.asList(new Object[] {"Hello", "World"}));
+        Object o = e.evaluate(jc);
+        assertEquals("Result is not last evaluated expression", "World", o);
+    }
+
+    public void testForEachWithEnumeration() throws Exception {
+        Expression e = JEXL.createExpression("for(item : list) item");
+        JexlContext jc = new MapContext();
+        jc.set("list", new StringTokenizer("Hello,World", ","));
+        Object o = e.evaluate(jc);
+        assertEquals("Result is not last evaluated expression", "World", o);
+    }
+
+    public void testForEachWithIterator() throws Exception {
+        Expression e = JEXL.createExpression("for(item : list) item");
+        JexlContext jc = new MapContext();
+        jc.set("list", Arrays.asList(new Object[] {"Hello", "World"}).iterator());
+        Object o = e.evaluate(jc);
+        assertEquals("Result is not last evaluated expression", "World", o);
+    }
+
+    public void testForEachWithMap() throws Exception {
+        Expression e = JEXL.createExpression("for(item : list) item");
+        JexlContext jc = new MapContext();
+        Map<?, ?> map = System.getProperties();
+        String lastProperty = (String) new ArrayList<Object>(map.values()).get(System.getProperties().size() - 1);
+        jc.set("list", map);
+        Object o = e.evaluate(jc);
+        assertEquals("Result is not last evaluated expression", lastProperty, o);
+    }
+
+    public void testForEachWithBlock() throws Exception {
+        Expression exs0 = JEXL.createExpression("for(in : list) { x = x + in; }");
+        Expression exs1 = JEXL.createExpression("foreach(item in list) { x = x + item; }");
+        Expression []exs = { exs0, exs1 };
+        JexlContext jc = new MapContext();
+        jc.set("list", new Object[] {"2", "3"});
+        for(int ex = 0; ex < exs.length; ++ex) {
+            jc.set("x", new Integer(1));
+            Object o = exs[ex].evaluate(jc);
+            assertEquals("Result is wrong", new Integer(6), o);
+            assertEquals("x is wrong", new Integer(6), jc.get("x"));
+        }
+    }
+
+    public void testForEachWithListExpression() throws Exception {
+        Expression e = JEXL.createExpression("for(item : list.keySet()) item");
+        JexlContext jc = new MapContext();
+        Map<?, ?> map = System.getProperties();
+        String lastKey = (String) new ArrayList<Object>(map.keySet()).get(System.getProperties().size() - 1);
+        jc.set("list", map);
+        Object o = e.evaluate(jc);
+        assertEquals("Result is not last evaluated expression", lastKey, o);
+    }
+    
+    public void testForEachWithProperty() throws Exception {
+        Expression e = JEXL.createExpression("for(item : list.cheeseList) item");
+        JexlContext jc = new MapContext();
+        jc.set("list", new Foo());
+        Object o = e.evaluate(jc);
+        assertEquals("Result is not last evaluated expression", "brie", o);
+    }
+    
+    public void testForEachWithIteratorMethod() throws Exception {
+        Expression e = JEXL.createExpression("for(item : list.cheezy) item");
+        JexlContext jc = new MapContext();
+        jc.set("list", new Foo());
+        Object o = e.evaluate(jc);
+        assertEquals("Result is not last evaluated expression", "brie", o);
+    }
+}
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/IfTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/IfTest.java
new file mode 100644
index 0000000..94eb37d
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/IfTest.java
@@ -0,0 +1,279 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2;
+
+
+/**
+ * Test cases for the if statement.
+ * 
+ * @author Dion Gillard
+ * @since 1.1
+ */
+public class IfTest extends JexlTestCase {
+
+    public IfTest(String testName) {
+        super(testName);
+    }
+
+    /**
+     * Make sure if true executes the true statement
+     * 
+     * @throws Exception on any error
+     */
+    public void testSimpleIfTrue() throws Exception {
+        Expression e = JEXL.createExpression("if (true) 1");
+        JexlContext jc = new MapContext();
+
+        Object o = e.evaluate(jc);
+        assertEquals("Result is not 1", new Integer(1), o);
+    }
+
+    /**
+     * Make sure if false doesn't execute the true statement
+     * 
+     * @throws Exception on any error
+     */
+    public void testSimpleIfFalse() throws Exception {
+        Expression e = JEXL.createExpression("if (false) 1");
+        JexlContext jc = new MapContext();
+
+        Object o = e.evaluate(jc);
+        assertNull("Return value is not empty", o);
+    }
+
+    /**
+     * Make sure if false executes the false statement
+     * 
+     * @throws Exception on any error
+     */
+    public void testSimpleElse() throws Exception {
+        Expression e = JEXL
+                .createExpression("if (false) 1 else 2;");
+        JexlContext jc = new MapContext();
+
+        Object o = e.evaluate(jc);
+        assertEquals("Result is not 2", new Integer(2), o);
+    }
+
+    /**
+     * Test the if statement handles blocks correctly
+     * 
+     * @throws Exception on any error
+     */
+    public void testBlockIfTrue() throws Exception {
+        Expression e = JEXL
+                .createExpression("if (true) { 'hello'; }");
+        JexlContext jc = new MapContext();
+
+        Object o = e.evaluate(jc);
+        assertEquals("Result is wrong", "hello", o);
+    }
+
+    /**
+     * Test the if statement handles blocks in the else statement correctly
+     * 
+     * @throws Exception on any error
+     */
+    public void testBlockElse() throws Exception {
+        Expression e = JEXL
+                .createExpression("if (false) {1} else {2 ; 3}");
+        JexlContext jc = new MapContext();
+
+        Object o = e.evaluate(jc);
+        assertEquals("Result is wrong", new Integer(3), o);
+    }
+
+    /**
+     * Test the if statement evaluates expressions correctly
+     * 
+     * @throws Exception on any error
+     */
+    public void testIfWithSimpleExpression() throws Exception {
+        Expression e = JEXL
+                .createExpression("if (x == 1) true;");
+        JexlContext jc = new MapContext();
+        jc.set("x", new Integer(1));
+
+        Object o = e.evaluate(jc);
+        assertEquals("Result is not true", Boolean.TRUE, o);
+    }
+
+    /**
+     * Test the if statement evaluates arithmetic expressions correctly
+     * 
+     * @throws Exception on any error
+     */
+    public void testIfWithArithmeticExpression() throws Exception {
+        Expression e = JEXL
+                .createExpression("if ((x * 2) + 1 == 5) true;");
+        JexlContext jc = new MapContext();
+        jc.set("x", new Integer(2));
+
+        Object o = e.evaluate(jc);
+        assertEquals("Result is not true", Boolean.TRUE, o);
+    }
+
+    /**
+     * Test the if statement evaluates decimal arithmetic expressions correctly
+     * 
+     * @throws Exception on any error
+     */
+    public void testIfWithDecimalArithmeticExpression() throws Exception {
+        Expression e = JEXL
+                .createExpression("if ((x * 2) == 5) true");
+        JexlContext jc = new MapContext();
+        jc.set("x", new Float(2.5f));
+
+        Object o = e.evaluate(jc);
+        assertEquals("Result is not true", Boolean.TRUE, o);
+    }
+
+    /**
+     * Test the if statement works with assignment
+     * 
+     * @throws Exception on any error
+     */
+    public void testIfWithAssignment() throws Exception {
+        Expression e = JEXL
+                .createExpression("if ((x * 2) == 5) {y = 1} else {y = 2;}");
+        JexlContext jc = new MapContext();
+        jc.set("x", new Float(2.5f));
+
+        e.evaluate(jc);
+        Object result = jc.get("y");
+        assertEquals("y has the wrong value", new Integer(1), result);
+    }
+
+    /**
+     * Ternary operator condition undefined or null evaluates to false
+     * independantly of engine flags.
+     * @throws Exception
+     */
+    public void testTernary() throws Exception {
+        JexlEngine jexl = new JexlEngine();
+        jexl.setCache(64);
+        JexlContext jc = new MapContext();
+        Expression e = jexl.createExpression("x.y.z = foo ?'bar':'quux'");
+        Object o;
+
+        // undefined foo
+
+        for(int l = 0; l < 4; ++l) {
+            jexl.setLenient((l & 1) != 0);
+            jexl.setSilent((l & 2) != 0);
+            o = e.evaluate(jc);
+            assertEquals("Should be quux", "quux", o);
+            o = jc.get("x.y.z");
+            assertEquals("Should be quux", "quux", o);
+        }
+
+        jc.set("foo", null);
+
+        for(int l = 0; l < 4; ++l) {
+            jexl.setLenient((l & 1) != 0);
+            jexl.setSilent((l & 2) != 0);
+            o = e.evaluate(jc);
+            assertEquals("Should be quux", "quux", o);
+            o = jc.get("x.y.z");
+            assertEquals("Should be quux", "quux", o);
+        }
+
+        jc.set("foo",Boolean.FALSE);
+
+        for(int l = 0; l < 4; ++l) {
+            jexl.setLenient((l & 1) != 0);
+            jexl.setSilent((l & 2) != 0);
+            o = e.evaluate(jc);
+            assertEquals("Should be quux", "quux", o);
+            o = jc.get("x.y.z");
+            assertEquals("Should be quux", "quux", o);
+        }
+
+        jc.set("foo",Boolean.TRUE);
+
+        for(int l = 0; l < 4; ++l) {
+            jexl.setLenient((l & 1) != 0);
+            jexl.setSilent((l & 2) != 0);
+            o = e.evaluate(jc);
+            assertEquals("Should be bar", "bar", o);
+            o = jc.get("x.y.z");
+            assertEquals("Should be bar", "bar", o);
+        }
+
+        debuggerCheck(jexl);
+    }
+
+    /**
+     * Ternary operator condition undefined or null evaluates to false
+     * independantly of engine flags.
+     * @throws Exception
+     */
+    public void testTernaryShorthand() throws Exception {
+        JexlEngine jexl = new JexlEngine();
+        jexl.setCache(64);
+        JexlContext jc = new MapContext();
+        Expression e = JEXL.createExpression("x.y.z = foo?:'quux'");
+        Object o;
+
+        // undefined foo
+
+        for(int l = 0; l < 4; ++l) {
+            jexl.setLenient((l & 1) != 0);
+            jexl.setSilent((l & 2) != 0);
+            o = e.evaluate(jc);
+            assertEquals("Should be quux", "quux", o);
+            o = jc.get("x.y.z");
+            assertEquals("Should be quux", "quux", o);
+        }
+
+        jc.set("foo", null);
+
+        for(int l = 0; l < 4; ++l) {
+            jexl.setLenient((l & 1) != 0);
+            jexl.setSilent((l & 2) != 0);
+            o = e.evaluate(jc);
+            assertEquals("Should be quux", "quux", o);
+            o = jc.get("x.y.z");
+            assertEquals("Should be quux", "quux", o);
+        }
+
+        jc.set("foo", Boolean.FALSE);
+
+        for(int l = 0; l < 4; ++l) {
+            jexl.setLenient((l & 1) != 0);
+            jexl.setSilent((l & 2) != 0);
+            o = e.evaluate(jc);
+            assertEquals("Should be quux", "quux", o);
+            o = jc.get("x.y.z");
+            assertEquals("Should be quux", "quux", o);
+        }
+
+        jc.set("foo","bar");
+        
+        for(int l = 0; l < 4; ++l) {
+            jexl.setLenient((l & 1) != 0);
+            jexl.setSilent((l & 2) != 0);
+            o = e.evaluate(jc);
+            assertEquals("Should be bar", "bar", o);
+            o = jc.get("x.y.z");
+            assertEquals("Should be bar", "bar", o);
+        }
+
+        debuggerCheck(jexl);
+    }
+}
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/IssuesTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/IssuesTest.java
new file mode 100644
index 0000000..9bdcdd4
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/IssuesTest.java
@@ -0,0 +1,335 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+import org.apache.commons.jexl2.introspection.Uberspect;
+import org.apache.commons.jexl2.internal.Introspector;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.commons.jexl2.introspection.UberspectImpl;
+
+/**
+ * Test cases for reported issues
+ */
+public class IssuesTest extends JexlTestCase {
+    @Override
+    public void setUp() throws Exception {
+        // ensure jul logging is only error to avoid warning in silent mode
+        //java.util.logging.Logger.getLogger(JexlEngine.class.getName()).setLevel(java.util.logging.Level.SEVERE);
+    }
+
+    // JEXL-49: blocks not parsed (fixed)
+    public void test49() throws Exception {
+        Map<String,Object> vars = new HashMap<String,Object>();
+        JexlContext ctxt = new MapContext(vars);
+        String stmt = "{a = 'b'; c = 'd';}";
+        Script expr = JEXL.createScript(stmt);
+        /* Object value = */ expr.execute(ctxt);
+        assertTrue("JEXL-49 is not fixed", vars.get("a").equals("b") && vars.get("c").equals("d"));
+    }
+
+    // JEXL-48: bad assignment detection
+    public static class Another {
+        public String name = "whatever";
+        private Boolean foo = Boolean.TRUE;
+
+        public Boolean foo() {
+            return foo;
+        }
+
+        public int goo() {
+            return 100;
+        }
+    }
+
+    public static class Foo {
+        private Another inner;
+
+        Foo() {
+            inner = new Another();
+        }
+
+        public Another getInner() {
+            return inner;
+        }
+    }
+
+    public void test48() throws Exception {
+        JexlEngine jexl = new JexlEngine();
+        // ensure errors will throw
+        jexl.setSilent(false);
+        String jexlExp = "(foo.getInner().foo() eq true) and (foo.getInner().goo() = (foo.getInner().goo()+1-1))";
+        Expression e = jexl.createExpression(jexlExp);
+        JexlContext jc = new MapContext();
+        jc.set("foo", new Foo());
+
+        try {
+            /* Object o = */ e.evaluate(jc);
+            fail("Should have failed due to invalid assignment");
+        } catch (JexlException xjexl) {
+            // expected
+        }
+    }
+
+    // JEXL-47: C style comments (single & multi line) (fixed in Parser.jjt)
+    // JEXL-44: comments dont allow double quotes (fixed in Parser.jjt)
+    public void test47() throws Exception {
+        JexlEngine jexl = new JexlEngine();
+        // ensure errors will throw
+        jexl.setSilent(false);
+        JexlContext ctxt = new MapContext();
+
+        Expression expr = jexl.createExpression("true//false\n");
+        Object value = expr.evaluate(ctxt);
+        assertTrue("should be true", ((Boolean) value).booleanValue());
+
+        expr = jexl.createExpression("/*true*/false");
+        value = expr.evaluate(ctxt);
+        assertFalse("should be false", ((Boolean) value).booleanValue());
+
+        expr = jexl.createExpression("/*\"true\"*/false");
+        value = expr.evaluate(ctxt);
+        assertFalse("should be false", ((Boolean) value).booleanValue());
+    }
+
+    // JEXL-42: NullPointerException evaluating an expression
+    // fixed in JexlArithmetic by allowing add operator to deal with string, null
+    public void test42() throws Exception {
+        JexlEngine jexl = new JexlEngine();
+        UnifiedJEXL uel = new UnifiedJEXL(jexl);
+        // ensure errors will throw
+        //jexl.setSilent(false);
+        JexlContext ctxt = new MapContext();
+        ctxt.set("ax", "ok");
+
+        UnifiedJEXL.Expression expr = uel.parse("${ax+(bx)}");
+        Object value = expr.evaluate(ctxt);
+        assertTrue("should be ok", "ok".equals(value));
+    }
+
+    // JEXL-40: failed to discover all methods (non public class implements public method)
+    // fixed in ClassMap by taking newer version of populateCache from Velocity
+    public static abstract class Base {
+        public abstract boolean foo();
+    }
+
+    class Derived extends Base {
+        @Override
+        public boolean foo() {
+            return true;
+        }
+    }
+
+    public void test40() throws Exception {
+        JexlEngine jexl = new JexlEngine();
+        // ensure errors will throw
+        jexl.setSilent(false);
+        JexlContext ctxt = new MapContext();
+        ctxt.set("derived", new Derived());
+
+        Expression expr = jexl.createExpression("derived.foo()");
+        Object value = expr.evaluate(ctxt);
+        assertTrue("should be true", ((Boolean) value).booleanValue());
+    }
+
+    // JEXL-52: can be implemented by deriving Interpreter.{g,s}etAttribute; later
+    public void test52base() throws Exception {
+        JexlEngine jexl = new JexlEngine();
+        Uberspect uber = jexl.getUberspect();
+        // most likely, call will be in an Interpreter, getUberspect
+        String[] names = ((Introspector) uber).getMethodNames(Another.class);
+        assertTrue("should find methods", names.length > 0);
+        int found = 0;
+        for (String name : names) {
+            if ("foo".equals(name) || "goo".equals(name)) {
+                found += 1;
+            }
+        }
+        assertTrue("should have foo & goo", found == 2);
+
+        names = ((UberspectImpl) uber).getFieldNames(Another.class);
+        assertTrue("should find fields", names.length > 0);
+        found = 0;
+        for (String name : names) {
+            if ("name".equals(name)) {
+                found += 1;
+            }
+        }
+        assertTrue("should have name", found == 1);
+    }
+
+    // JEXL-10/JEXL-11: variable checking, null operand is error
+    public void test11() throws Exception {
+        JexlEngine jexl = new JexlEngine();
+        // ensure errors will throw
+        jexl.setSilent(false);
+        jexl.setLenient(false);
+        JexlContext ctxt = new MapContext();
+        ctxt.set("a", null);
+
+        String[] exprs = {
+            //"10 + null",
+            //"a - 10",
+            //"b * 10",
+            "a % b"//,
+            //"1000 / a"
+        };
+        for (int e = 0; e < exprs.length; ++e) {
+            try {
+                Expression expr = jexl.createExpression(exprs[e]);
+                /* Object value = */ expr.evaluate(ctxt);
+                fail(exprs[e] + " : should have failed due to null argument");
+            } catch (JexlException xjexl) {
+                // expected
+            }
+        }
+    }
+
+    // JEXL-62
+    public void test62() throws Exception {
+        JexlContext ctxt;
+        JexlEngine jexl = new JexlEngine();
+        jexl.setSilent(true); // to avoid throwing JexlException on null method call
+
+        Script jscript;
+
+        ctxt = new MapContext();
+        jscript = jexl.createScript("dummy.hashCode()");
+        assertEquals(jscript.getText(), null, jscript.execute(ctxt)); // OK
+
+        ctxt.set("dummy", "abcd");
+        assertEquals(jscript.getText(), Integer.valueOf("abcd".hashCode()), jscript.execute(ctxt)); // OK
+
+        jscript = jexl.createScript("dummy.hashCode");
+        assertEquals(jscript.getText(), null, jscript.execute(ctxt)); // OK
+
+        Expression jexpr;
+
+        ctxt = new MapContext();
+        jexpr = jexl.createExpression("dummy.hashCode()");
+        assertEquals(jexpr.getExpression(), null, jexpr.evaluate(ctxt)); // OK
+
+        ctxt.set("dummy", "abcd");
+        assertEquals(jexpr.getExpression(), Integer.valueOf("abcd".hashCode()), jexpr.evaluate(ctxt)); // OK
+
+        jexpr = jexl.createExpression("dummy.hashCode");
+        assertEquals(jexpr.getExpression(), null, jexpr.evaluate(ctxt)); // OK
+    }
+
+    // JEXL-73
+    public void test73() throws Exception {
+        JexlContext ctxt = new MapContext();
+        JexlEngine jexl = new JexlEngine();
+        jexl.setSilent(false);
+        jexl.setLenient(false);
+        Expression e;
+        e = jexl.createExpression("c.e");
+        try {
+            /* Object o = */ e.evaluate(ctxt);
+        } catch (JexlException xjexl) {
+            String msg = xjexl.getMessage();
+            assertTrue(msg.indexOf("variable c.e") > 0);
+        }
+
+        ctxt.set("c", "{ 'a' : 3, 'b' : 5}");
+        ctxt.set("e", Integer.valueOf(2));
+        try {
+            /* Object o = */ e.evaluate(ctxt);
+        } catch (JexlException xjexl) {
+            String msg = xjexl.getMessage();
+            assertTrue(msg.indexOf("variable c.e") > 0);
+        }
+
+    }
+
+    // JEXL-87
+    public void test87() throws Exception {
+        JexlContext ctxt = new MapContext();
+        JexlEngine jexl = new JexlEngine();
+        jexl.setSilent(false);
+        jexl.setLenient(false);
+        Expression divide = jexl.createExpression("l / r");
+        Expression modulo = jexl.createExpression("l % r");
+
+        ctxt.set("l", java.math.BigInteger.valueOf(7));
+        ctxt.set("r", java.math.BigInteger.valueOf(2));
+        assertEquals("3", divide.evaluate(ctxt).toString());
+        assertEquals("1", modulo.evaluate(ctxt).toString());
+
+        ctxt.set("l", java.math.BigDecimal.valueOf(7));
+        ctxt.set("r", java.math.BigDecimal.valueOf(2));
+        assertEquals("3.5", divide.evaluate(ctxt).toString());
+        assertEquals("1", modulo.evaluate(ctxt).toString());
+    }
+
+    // JEXL-90
+    public void test90() throws Exception {
+        JexlContext ctxt = new MapContext();
+        JexlEngine jexl = new JexlEngine();
+        jexl.setSilent(false);
+        jexl.setLenient(false);
+        jexl.setCache(16);
+        // ';' is necessary between expressions
+        String[] fexprs = {
+            "a=3 b=4",
+            "while(a) while(a)",
+            "1 2",
+            "if (true) 2; 3 {}",
+            "while (x) 1 if (y) 2 3"
+        };
+        for (int f = 0; f < fexprs.length; ++f) {
+            try {
+                jexl.createScript(fexprs[f]);
+                fail(fexprs[f] + ": Should have failed in parse");
+            } catch (JexlException xany) {
+                // expected to fail in parse
+            }
+        }
+        // ';' is necessary between expressions and only expressions
+        String[] exprs = {
+            "if (x) {1} if (y) {2}",
+            "if (x) 1 if (y) 2",
+            "while (x) 1 if (y) 2 else 3",
+            "for(z : [3, 4, 5]) { z } y ? 2 : 1",
+            "for(z : [3, 4, 5]) { z } if (y) 2 else 1"
+        };
+        ctxt.set("x", Boolean.FALSE);
+        ctxt.set("y", Boolean.TRUE);
+        for (int e = 0; e < exprs.length; ++e) {
+            Script s = jexl.createScript(exprs[e]);
+            assertEquals(Integer.valueOf(2), s.execute(ctxt));
+        }
+        debuggerCheck(jexl);
+    }
+
+    // JEXL-44
+    public void test44() throws Exception {
+        JexlContext ctxt = new MapContext();
+        JexlEngine jexl = new JexlEngine();
+        jexl.setSilent(false);
+        jexl.setLenient(false);
+        Script script;
+        script = jexl.createScript("'hello world!'//commented");
+        assertEquals("hello world!", script.execute(ctxt));
+        script = jexl.createScript("'hello world!';//commented\n'bye...'");
+        assertEquals("bye...", script.execute(ctxt));
+        script = jexl.createScript("'hello world!'## commented");
+        assertEquals("hello world!", script.execute(ctxt));
+        script = jexl.createScript("'hello world!';## commented\n'bye...'");
+        assertEquals("bye...", script.execute(ctxt));
+    }
+
+}
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/Jexl.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/Jexl.java
new file mode 100644
index 0000000..0a662aa
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/Jexl.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2;
+
+import java.util.Map;
+
+/**
+ * @author Dion Gillard
+ * @since 1.0
+ * Command line interface for Jexl for use in testing
+ */
+public class Jexl {
+
+    public static void main(String[] args) {
+        final JexlEngine JEXL = new JexlEngine();
+        Map<Object,Object> m = System.getProperties();
+        // dummy context to get variables
+        JexlContext context = new MapContext();
+        for(Map.Entry<Object,Object> e : m.entrySet()) {
+            context.set(e.getKey().toString(), e.getValue());
+        }
+        try {
+            for (int i = 0; i < args.length; i++) {
+                Expression e = JEXL.createExpression(args[i]);
+                System.out.println("evaluate(" + args[i] + ") = '" + e.evaluate(context) + "'");
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/JexlTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/JexlTest.java
new file mode 100644
index 0000000..60fc601
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/JexlTest.java
@@ -0,0 +1,825 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+import java.io.StringReader;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.jexl2.parser.Parser;
+
+/**
+ *  Simple testcases
+ *
+ *  @since 1.0
+ *  @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
+ *  @version $Id$
+ */
+public class JexlTest extends JexlTestCase
+{
+    protected static final String METHOD_STRING = "Method string";
+    protected static final String GET_METHOD_STRING = "GetMethod string";
+
+    public JexlTest(String testName)
+    {
+        super(testName);
+    }
+
+    /**
+      *  test a simple property expression
+      */
+    public void testProperty()
+         throws Exception
+    {
+        /*
+         *  tests a simple property expression
+         */
+
+        Expression e = JEXL.createExpression("foo.bar");
+        JexlContext jc = new MapContext();
+
+        jc.set("foo", new Foo() );
+        Object o = e.evaluate(jc);
+
+        assertTrue("o not instanceof String", o instanceof String);
+        assertEquals("o incorrect", GET_METHOD_STRING, o);
+    }
+
+    public void testBoolean()
+         throws Exception
+    {
+        JexlContext jc = new MapContext();
+        jc.set("foo", new Foo() );
+        jc.set("a", Boolean.TRUE);
+        jc.set("b", Boolean.FALSE);
+
+        assertExpression(jc, "foo.convertBoolean(a==b)", "Boolean : false");
+        assertExpression(jc, "foo.convertBoolean(a==true)", "Boolean : true");
+        assertExpression(jc, "foo.convertBoolean(a==false)", "Boolean : false");
+        assertExpression(jc, "foo.convertBoolean(true==false)", "Boolean : false");
+        assertExpression(jc, "true eq false", Boolean.FALSE);
+        assertExpression(jc, "true ne false", Boolean.TRUE);
+    }
+
+    public void testStringLit()
+         throws Exception
+    {
+        /*
+         *  tests a simple property expression
+         */
+        JexlContext jc = new MapContext();
+        jc.set("foo", new Foo() );
+        assertExpression(jc, "foo.get(\"woogie\")", "Repeat : woogie");
+    }
+
+    public void testExpression()
+         throws Exception
+    {
+        JexlContext jc = new MapContext();
+        jc.set("foo", new Foo() );
+        jc.set("a", Boolean.TRUE);
+        jc.set("b", Boolean.FALSE);
+        jc.set("num", new Integer(5));
+        jc.set("now", Calendar.getInstance().getTime());
+        GregorianCalendar gc = new GregorianCalendar(5000, 11, 20);
+        jc.set("now2", gc.getTime());
+        jc.set("bdec", new BigDecimal("7"));
+        jc.set("bint", new BigInteger("7"));
+
+        assertExpression(jc, "a == b", Boolean.FALSE);
+        assertExpression(jc, "a==true", Boolean.TRUE);
+        assertExpression(jc, "a==false", Boolean.FALSE);
+        assertExpression(jc, "true==false", Boolean.FALSE);
+        
+        assertExpression(jc, "2 < 3", Boolean.TRUE);
+        assertExpression(jc, "num < 5", Boolean.FALSE);
+        assertExpression(jc, "num < num", Boolean.FALSE);
+        assertExpression(jc, "num < null", Boolean.FALSE);
+        assertExpression(jc, "num < 2.5", Boolean.FALSE);
+        assertExpression(jc, "now2 < now", Boolean.FALSE); // test comparable
+//
+        assertExpression(jc, "'6' <= '5'", Boolean.FALSE);
+        assertExpression(jc, "num <= 5", Boolean.TRUE);
+        assertExpression(jc, "num <= num", Boolean.TRUE);
+        assertExpression(jc, "num <= null", Boolean.FALSE);
+        assertExpression(jc, "num <= 2.5", Boolean.FALSE);
+        assertExpression(jc, "now2 <= now", Boolean.FALSE); // test comparable
+
+//        
+        assertExpression(jc, "'6' >= '5'", Boolean.TRUE);
+        assertExpression(jc, "num >= 5", Boolean.TRUE);
+        assertExpression(jc, "num >= num", Boolean.TRUE);
+        assertExpression(jc, "num >= null", Boolean.FALSE);
+        assertExpression(jc, "num >= 2.5", Boolean.TRUE);
+        assertExpression(jc, "now2 >= now", Boolean.TRUE); // test comparable
+
+        assertExpression(jc, "'6' > '5'", Boolean.TRUE);
+        assertExpression(jc, "num > 4", Boolean.TRUE);
+        assertExpression(jc, "num > num", Boolean.FALSE);
+        assertExpression(jc, "num > null", Boolean.FALSE);
+        assertExpression(jc, "num > 2.5", Boolean.TRUE);
+        assertExpression(jc, "now2 > now", Boolean.TRUE); // test comparable
+
+        assertExpression(jc, "\"foo\" + \"bar\" == \"foobar\"", Boolean.TRUE);
+
+        assertExpression(jc, "bdec > num", Boolean.TRUE);
+        assertExpression(jc, "bdec >= num", Boolean.TRUE);
+        assertExpression(jc, "num <= bdec", Boolean.TRUE);
+        assertExpression(jc, "num < bdec", Boolean.TRUE);
+        assertExpression(jc, "bint > num", Boolean.TRUE);
+        assertExpression(jc, "bint == bdec", Boolean.TRUE);
+        assertExpression(jc, "bint >= num", Boolean.TRUE);
+        assertExpression(jc, "num <= bint", Boolean.TRUE);
+        assertExpression(jc, "num < bint", Boolean.TRUE);
+    }
+
+    public void testEmpty()
+         throws Exception
+    {
+        JexlContext jc = new MapContext();
+        jc.set("string", "");
+        jc.set("array", new Object[0]);
+        jc.set("map", new HashMap<Object, Object>());
+        jc.set("list", new ArrayList<Object>());
+        jc.set("set", (new HashMap<Object, Object>()).keySet());
+        jc.set("longstring", "thingthing");
+
+        /*
+         *  I can't believe anyone thinks this is a syntax.. :)
+         */
+        assertExpression(jc, "empty nullthing", Boolean.TRUE);
+        assertExpression(jc, "empty string", Boolean.TRUE);
+        assertExpression(jc, "empty array", Boolean.TRUE);
+        assertExpression(jc, "empty map", Boolean.TRUE);
+        assertExpression(jc, "empty set", Boolean.TRUE);
+        assertExpression(jc, "empty list", Boolean.TRUE);
+        assertExpression(jc, "empty longstring", Boolean.FALSE);
+        assertExpression(jc, "not empty longstring", Boolean.TRUE);
+    }
+
+    public void testSize()
+         throws Exception
+    {
+        JexlContext jc = new MapContext();
+        jc.set("s", "five!");
+        jc.set("array", new Object[5]);
+
+        Map<String, Integer> map = new HashMap<String, Integer>();
+
+        map.put("1", new Integer(1));
+        map.put("2", new Integer(2));
+        map.put("3", new Integer(3));
+        map.put("4", new Integer(4));
+        map.put("5", new Integer(5));
+
+        jc.set("map", map);
+
+        List<String> list = new ArrayList<String>();
+
+        list.add("1");
+        list.add("2");
+        list.add("3");
+        list.add("4");
+        list.add("5");
+
+        jc.set("list", list);
+
+        // 30652 - support for set
+        Set<String> set = new HashSet<String>();
+        set.addAll(list);
+        set.add("1");
+        
+        jc.set("set", set);
+        
+        // support generic int size() method
+        BitSet bitset = new BitSet(5);
+        jc.set("bitset", bitset);
+
+        assertExpression(jc, "size(s)", new Integer(5));
+        assertExpression(jc, "size(array)", new Integer(5));
+        assertExpression(jc, "size(list)", new Integer(5));
+        assertExpression(jc, "size(map)", new Integer(5));
+        assertExpression(jc, "size(set)", new Integer(5));
+        assertExpression(jc, "size(bitset)", new Integer(64));
+        assertExpression(jc, "list.size()", new Integer(5));
+        assertExpression(jc, "map.size()", new Integer(5));
+        assertExpression(jc, "set.size()", new Integer(5));
+        assertExpression(jc, "bitset.size()", new Integer(64));
+
+        assertExpression(jc, "list.get(size(list) - 1)", "5");
+        assertExpression(jc, "list[size(list) - 1]", "5");
+        assertExpression(jc, "list.get(list.size() - 1)", "5");
+    }
+
+    public void testSizeAsProperty() throws Exception
+    {
+        JexlContext jc = new MapContext();
+        Map<String, Object> map = new HashMap<String, Object>();
+        map.put("size", "cheese");
+        jc.set("map", map);
+        jc.set("foo", new Foo());
+
+        assertExpression(jc, "map['size']", "cheese");
+// PR - unsure whether or not we should support map.size or force usage of the above 'escaped' version        
+//        assertExpression(jc, "map.size", "cheese");
+        assertExpression(jc, "foo.getSize()", new Integer(22));
+        // failing assertion for size property
+        //assertExpression(jc, "foo.size", new Integer(22));
+    }
+
+    /**
+      *  test the new function e.g constructor invocation
+      */
+    public void testNew() throws Exception {
+        JexlContext jc = new MapContext();
+        jc.set("double", Double.class);
+        jc.set("foo", "org.apache.commons.jexl2.Foo");
+        Expression expr;
+        Object value;
+        expr = JEXL.createExpression("new(double, 1)");
+        value = expr.evaluate(jc);
+        assertEquals(expr.toString(), new Double(1.0), value);
+        expr = JEXL.createExpression("new('java.lang.Float', 100)");
+        value = expr.evaluate(jc);
+        assertEquals(expr.toString(), new Float(100.0), value);
+        expr = JEXL.createExpression("new(foo).quux");
+        value = expr.evaluate(jc);
+        assertEquals(expr.toString(), "Repeat : quux", value);
+    }
+
+    /**
+      *  test some simple mathematical calculations
+      */
+    public void testCalculations()
+         throws Exception
+    {
+        JexlContext jc = new MapContext();
+
+        /*
+         * test to ensure new string cat works
+         */
+        jc.set("stringy", "thingy" );
+        assertExpression(jc, "stringy + 2", "thingy2");
+
+        /*
+         * test new null coersion
+         */
+        jc.set("imanull", null );
+        assertExpression(jc, "imanull + 2", new Integer(2));
+        assertExpression(jc, "imanull + imanull", new Integer(0));
+        
+        /* test for bugzilla 31577 */
+        jc.set("n", new Integer(0));
+        assertExpression(jc, "n != null && n != 0", Boolean.FALSE);
+    }
+
+    /**
+      *  test some simple conditions
+      */
+    public void testConditions()
+         throws Exception
+    {
+        JexlContext jc = new MapContext();
+        jc.set("foo", new Integer(2) );
+        jc.set("aFloat", new Float(1));
+        jc.set("aDouble", new Double(2));
+        jc.set("aChar", new Character('A'));
+        jc.set("aBool", Boolean.TRUE);
+        StringBuffer buffer = new StringBuffer("abc");
+        List<Object> list = new ArrayList<Object>();
+        List<Object> list2 = new LinkedList<Object>();
+        jc.set("aBuffer", buffer);
+        jc.set("aList", list);
+        jc.set("bList", list2);
+        
+        assertExpression(jc, "foo == 2", Boolean.TRUE);
+        assertExpression(jc, "2 == 3", Boolean.FALSE);
+        assertExpression(jc, "3 == foo", Boolean.FALSE);
+        assertExpression(jc, "3 != foo", Boolean.TRUE);
+        assertExpression(jc, "foo != 2", Boolean.FALSE);
+        // test float and double equality
+        assertExpression(jc, "aFloat eq aDouble", Boolean.FALSE);
+        assertExpression(jc, "aFloat ne aDouble", Boolean.TRUE);
+        assertExpression(jc, "aFloat == aDouble", Boolean.FALSE);
+        assertExpression(jc, "aFloat != aDouble", Boolean.TRUE);
+        // test number and character equality
+        assertExpression(jc, "foo == aChar", Boolean.FALSE);
+        assertExpression(jc, "foo != aChar", Boolean.TRUE);
+        // test string and boolean
+        assertExpression(jc, "aBool == 'true'", Boolean.TRUE);
+        assertExpression(jc, "aBool == 'false'", Boolean.FALSE);
+        assertExpression(jc, "aBool != 'false'", Boolean.TRUE);
+        // test null and boolean
+        assertExpression(jc, "aBool == notThere", Boolean.FALSE);
+        assertExpression(jc, "aBool != notThere", Boolean.TRUE);
+        // anything and string as a string comparison
+        assertExpression(jc, "aBuffer == 'abc'", Boolean.TRUE);
+        assertExpression(jc, "aBuffer != 'abc'", Boolean.FALSE);
+        // arbitrary equals
+        assertExpression(jc, "aList == bList", Boolean.TRUE);
+        assertExpression(jc, "aList != bList", Boolean.FALSE);
+    }
+
+    /**
+      *  test some simple conditions
+      */
+    public void testNotConditions()
+         throws Exception
+    {
+        JexlContext jc = new MapContext();
+
+        Foo foo = new Foo();
+        jc.set("x", Boolean.TRUE );
+        jc.set("foo", foo );
+        jc.set("bar", "true" );
+
+        assertExpression(jc, "!x", Boolean.FALSE);
+        assertExpression(jc, "x", Boolean.TRUE);
+        assertExpression(jc, "!bar", Boolean.FALSE);
+        assertExpression(jc, "!foo.isSimple()", Boolean.FALSE);
+        assertExpression(jc, "foo.isSimple()", Boolean.TRUE);
+        assertExpression(jc, "!foo.simple", Boolean.FALSE);
+        assertExpression(jc, "foo.simple", Boolean.TRUE);
+        assertExpression(jc, "foo.getCheeseList().size() == 3", Boolean.TRUE);
+        assertExpression(jc, "foo.cheeseList.size() == 3", Boolean.TRUE);
+
+        jc.set("string", "");
+        assertExpression(jc, "not empty string", Boolean.FALSE);
+        assertExpression(jc, "not(empty string)", Boolean.FALSE);
+        assertExpression(jc, "not empty(string)", Boolean.FALSE);
+        assertExpression(jc, "! empty string", Boolean.FALSE);
+        assertExpression(jc, "!(empty string)", Boolean.FALSE);
+        assertExpression(jc, "!empty(string)", Boolean.FALSE);
+
+    }
+
+
+    /**
+      *  test some simple conditions
+      */
+    public void testNotConditionsWithDots()
+         throws Exception
+    {
+        JexlContext jc = new MapContext();
+
+        jc.set("x.a", Boolean.TRUE );
+        jc.set("x.b", Boolean.FALSE );
+
+        assertExpression(jc, "x.a", Boolean.TRUE);
+        assertExpression(jc, "!x.a", Boolean.FALSE);
+        assertExpression(jc, "!x.b", Boolean.TRUE);
+    }
+
+    /**
+      *  test some simple conditions
+      */
+    public void testComparisons()
+         throws Exception
+    {
+        JexlContext jc = new MapContext();
+        jc.set("foo", "the quick and lazy fox" );
+
+        assertExpression(jc, "foo.indexOf('quick') > 0", Boolean.TRUE);
+        assertExpression(jc, "foo.indexOf('bar') >= 0", Boolean.FALSE);
+        assertExpression(jc, "foo.indexOf('bar') < 0", Boolean.TRUE);
+    }
+
+    /**
+      *  test some null conditions
+      */
+    public void testNull()
+         throws Exception
+    {
+        JexlContext jc = new MapContext();
+        jc.set("bar", new Integer(2) );
+
+        assertExpression(jc, "empty foo", Boolean.TRUE);
+        assertExpression(jc, "bar == null", Boolean.FALSE);
+        assertExpression(jc, "foo == null", Boolean.TRUE);
+        assertExpression(jc, "bar != null", Boolean.TRUE);
+        assertExpression(jc, "foo != null", Boolean.FALSE);
+        assertExpression(jc, "empty(bar)", Boolean.FALSE);
+        assertExpression(jc, "empty(foo)", Boolean.TRUE);
+    }
+
+    /** 
+     * test quoting in strings 
+     */
+    public void testStringQuoting() throws Exception {
+        JexlContext jc = new MapContext();
+        assertExpression(jc, "'\"Hello\"'", "\"Hello\"");
+        assertExpression(jc, "\"I'm testing\"", "I'm testing");
+    }
+    
+    /**
+      *  test some blank strings
+      */
+    public void testBlankStrings()
+         throws Exception
+    {
+        JexlContext jc = new MapContext();
+        jc.set("bar", "" );
+
+        assertExpression(jc, "foo == ''", Boolean.FALSE);
+        assertExpression(jc, "bar == ''", Boolean.TRUE);
+        assertExpression(jc, "barnotexist == ''", Boolean.FALSE);
+        assertExpression(jc, "empty bar", Boolean.TRUE);
+        assertExpression(jc, "bar.length() == 0", Boolean.TRUE);
+        assertExpression(jc, "size(bar) == 0", Boolean.TRUE);
+    }
+
+    /**
+      *  test some blank strings
+      */
+    public void testLogicExpressions()
+         throws Exception
+    {
+        JexlContext jc = new MapContext();
+        jc.set("foo", "abc" );
+        jc.set("bar", "def" );
+
+        assertExpression(jc, "foo == 'abc' || bar == 'abc'", Boolean.TRUE);
+        assertExpression(jc, "foo == 'abc' or bar == 'abc'", Boolean.TRUE);
+        assertExpression(jc, "foo == 'abc' && bar == 'abc'", Boolean.FALSE);
+        assertExpression(jc, "foo == 'abc' and bar == 'abc'", Boolean.FALSE);
+
+        assertExpression(jc, "foo == 'def' || bar == 'abc'", Boolean.FALSE);
+        assertExpression(jc, "foo == 'def' or bar == 'abc'", Boolean.FALSE);
+        assertExpression(jc, "foo == 'abc' && bar == 'def'", Boolean.TRUE);
+        assertExpression(jc, "foo == 'abc' and bar == 'def'", Boolean.TRUE);
+    }
+
+
+    /**
+      *  test variables with underscore names
+      */
+    public void testVariableNames()
+         throws Exception
+    {
+        JexlContext jc = new MapContext();
+        jc.set("foo_bar", "123" );
+        
+        assertExpression(jc, "foo_bar", "123");
+    }
+
+    /**
+      *  test the use of dot notation to lookup map entries
+      */
+    public void testMapDot()
+         throws Exception
+    {
+        Map<String, String> foo = new HashMap<String, String>();
+        foo.put( "bar", "123" );
+
+        JexlContext jc = new MapContext();
+        jc.set("foo", foo );
+        
+        assertExpression(jc, "foo.bar", "123");
+    }
+
+    /**
+     *  Tests string literals
+     */
+    public void testStringLiterals()
+        throws Exception
+    {
+        JexlContext jc = new MapContext();
+        jc.set("foo", "bar" );
+
+        assertExpression(jc, "foo == \"bar\"", Boolean.TRUE);
+        assertExpression(jc, "foo == 'bar'", Boolean.TRUE);
+    }
+
+    /**
+      *  test the use of an int based property
+      */
+    public void testIntProperty()
+         throws Exception
+    {
+        Foo foo = new Foo();
+
+        // lets check the square function first..
+        assertEquals(4, foo.square(2));
+        assertEquals(4, foo.square(-2));
+
+        JexlContext jc = new MapContext();
+        jc.set("foo", foo );
+
+        assertExpression(jc, "foo.count", new Integer(5));
+        assertExpression(jc, "foo.square(2)", new Integer(4));
+        assertExpression(jc, "foo.square(-2)", new Integer(4));
+    }
+
+    /**
+      *  test the -1 comparison bug
+      */
+    public void testNegativeIntComparison()
+         throws Exception
+    {
+        JexlContext jc = new MapContext();
+        Foo foo = new Foo();
+        jc.set("foo", foo );
+
+        assertExpression(jc, "foo.count != -1", Boolean.TRUE);
+        assertExpression(jc, "foo.count == 5", Boolean.TRUE);
+        assertExpression(jc, "foo.count == -1", Boolean.FALSE);
+    }
+
+    /**
+     * Attempts to recreate bug http://jira.werken.com/ViewIssue.jspa?key=JELLY-8
+     */
+    public void testCharAtBug()
+        throws Exception
+    {
+        JexlContext jc = new MapContext();
+
+        jc.set("foo", "abcdef");
+
+        assertExpression(jc, "foo.substring(2,4)", "cd");
+        assertExpression(jc, "foo.charAt(2)", new Character('c'));
+        assertExpression(jc, "foo.charAt(-2)", null);
+
+    }
+
+    public void testEmptyDottedVariableName() throws Exception
+    {
+        JexlContext jc = new MapContext();
+
+        jc.set( "this.is.a.test", "");
+
+        assertExpression(jc, "empty(this.is.a.test)", Boolean.TRUE);
+    }
+
+    public void testEmptySubListOfMap() throws Exception
+    {
+        JexlContext jc = new MapContext();
+        Map<String, ArrayList<?>> m = new HashMap<String, ArrayList<?>>();
+        m.put("aList", new ArrayList<Object>());
+
+        jc.set( "aMap", m );
+
+        assertExpression( jc, "empty( aMap.aList )", Boolean.TRUE );
+    }
+
+    public void testCoercionWithComparisionOperators()
+        throws Exception
+    {
+        JexlContext jc = new MapContext();
+
+        assertExpression(jc, "'2' > 1", Boolean.TRUE);
+        assertExpression(jc, "'2' >= 1", Boolean.TRUE);
+        assertExpression(jc, "'2' >= 2", Boolean.TRUE);
+        assertExpression(jc, "'2' < 1", Boolean.FALSE);
+        assertExpression(jc, "'2' <= 1", Boolean.FALSE);
+        assertExpression(jc, "'2' <= 2", Boolean.TRUE);
+
+        assertExpression(jc, "2 > '1'", Boolean.TRUE);
+        assertExpression(jc, "2 >= '1'", Boolean.TRUE);
+        assertExpression(jc, "2 >= '2'", Boolean.TRUE);
+        assertExpression(jc, "2 < '1'", Boolean.FALSE);
+        assertExpression(jc, "2 <= '1'", Boolean.FALSE);
+        assertExpression(jc, "2 <= '2'", Boolean.TRUE);
+    }
+
+    /**
+     * Test that 'and' only evaluates the second item if needed
+     * @throws Exception if there are errors
+     */
+    public void testBooleanShortCircuitAnd() throws Exception
+    {
+        // handle false for the left arg of 'and'
+        Foo tester = new Foo();
+        JexlContext jc = new MapContext();
+        jc.set("first", Boolean.FALSE);
+        jc.set("foo", tester);
+        Expression expr = JEXL.createExpression("first and foo.trueAndModify");
+        expr.evaluate(jc);
+        assertTrue("Short circuit failure: rhs evaluated when lhs FALSE", !tester.getModified());
+        // handle true for the left arg of 'and' 
+        tester = new Foo();
+        jc.set("first", Boolean.TRUE);
+        jc.set("foo", tester);
+        expr.evaluate(jc);
+        assertTrue("Short circuit failure: rhs not evaluated when lhs TRUE", tester.getModified());
+    }
+    
+    /**
+     * Test that 'or' only evaluates the second item if needed
+     * @throws Exception if there are errors
+     */
+    public void testBooleanShortCircuitOr() throws Exception
+    {
+        // handle false for the left arg of 'or'
+        Foo tester = new Foo();
+        JexlContext jc = new MapContext();
+        jc.set("first", Boolean.FALSE);
+        jc.set("foo", tester);
+        Expression expr = JEXL.createExpression("first or foo.trueAndModify");
+        expr.evaluate(jc);
+        assertTrue("Short circuit failure: rhs not evaluated when lhs FALSE", tester.getModified());
+        // handle true for the left arg of 'or' 
+        tester = new Foo();
+        jc.set("first", Boolean.TRUE);
+        jc.set("foo", tester);
+        expr.evaluate(jc);
+        assertTrue("Short circuit failure: rhs evaluated when lhs TRUE", !tester.getModified());
+    }
+
+    /**
+     * Simple test of '+' as a string concatenation operator
+     * @throws Exception
+     */
+    public void testStringConcatenation() throws Exception
+    {
+        JexlContext jc = new MapContext();
+        jc.set("first", "Hello");
+        jc.set("second", "World");
+        assertExpression(jc, "first + ' ' + second", "Hello World");
+    }
+
+    public void testToString() throws Exception {
+        String code = "abcd";
+        Expression expr = JEXL.createExpression(code);
+        assertEquals("Bad expression value", code, expr.toString());
+    }
+    
+    /**
+     * Make sure bad syntax throws ParseException
+     * @throws Exception on errors
+     */
+    public void testBadParse() throws Exception
+    {
+        try
+        {
+            assertExpression(new MapContext(), "empty()", null);
+            fail("Bad expression didn't throw ParseException");
+        }
+        catch (JexlException pe)
+        {
+            // expected behaviour
+        }
+    }
+
+    /**
+     * Test the ## comment in a string
+     * @throws Exception
+     */
+    public void testComment() throws Exception
+    {
+        assertExpression(new MapContext(), "## double or nothing\n 1 + 1", Integer.valueOf("2"));
+    }
+    
+    /**
+     * Test assignment.
+     * @throws Exception
+     */
+    public void testAssignment() throws Exception
+    {
+        JexlContext jc = new MapContext();
+        jc.set("aString", "Hello");
+        Foo foo = new Foo();
+        jc.set("foo", foo);
+        Parser parser = new Parser(new StringReader(";"));
+        parser.parse(new StringReader("aString = 'World';"), null);
+        
+        assertExpression(jc, "hello = 'world'", "world");
+        assertEquals("hello variable not changed", "world", jc.get("hello"));
+        assertExpression(jc, "result = 1 + 1", new Integer(2));
+        assertEquals("result variable not changed", new Integer(2), jc.get("result"));
+        // todo: make sure properties can be assigned to, fall back to flat var if no property
+        // assertExpression(jc, "foo.property1 = '99'", "99");
+        // assertEquals("property not set", "99", foo.getProperty1());
+    }
+    
+    public void testAntPropertiesWithMethods() throws Exception
+    {
+        JexlContext jc = new MapContext();
+        String value = "Stinky Cheese";
+        jc.set("maven.bob.food", value);
+        assertExpression(jc, "maven.bob.food.length()", new Integer(value.length()));
+        assertExpression(jc, "empty(maven.bob.food)", Boolean.FALSE);
+        assertExpression(jc, "size(maven.bob.food)", new Integer(value.length()));
+        assertExpression(jc, "maven.bob.food + ' is good'", value + " is good");
+
+        // DG: Note the following ant properties don't work
+//        String version = "1.0.3";
+//        jc.set("commons-logging", version);
+//        assertExpression(jc, "commons-logging", version);
+    }
+
+    public void testUnicodeSupport() throws Exception
+    {
+        JexlContext jc = new MapContext();
+        assertExpression(jc, "myvar == 'Użytkownik'", Boolean.FALSE);
+        assertExpression(jc, "'c:\\some\\windows\\path'", "c:\\some\\windows\\path");
+        assertExpression(jc, "'foo\\u0020bar'", "foo\u0020bar");
+        assertExpression(jc, "'foo\\u0020\\u0020bar'", "foo\u0020\u0020bar");
+        assertExpression(jc, "'\\u0020foobar\\u0020'", "\u0020foobar\u0020");
+    }
+
+    public static final class Duck {
+        int user = 10;
+        @SuppressWarnings("boxing")
+        public Integer get(String val) {
+            if ("zero".equals(val))
+                return 0;
+            if ("one".equals(val))
+                return 1;
+            if ("user".equals(val))
+                return user;
+            return -1;
+        }
+        @SuppressWarnings("boxing")
+        public void set(String val, Object value) {
+            if ("user".equals(val)) {
+                if ("zero".equals(value))
+                    user = 0;
+                else if ("one".equals(value))
+                    user = 1;
+                else
+                    user = value instanceof Integer? (Integer) value : -1;
+            }
+        }
+    }
+
+    @SuppressWarnings("boxing")
+    public void testDuck() throws Exception {
+        JexlEngine jexl = JEXL;
+        JexlContext jc = new MapContext();
+        jc.set("duck", new Duck());
+        Expression expr;
+        Object result;
+        expr = jexl.createExpression("duck.zero");
+        result = expr.evaluate(jc);
+        assertEquals(expr.toString(), 0, result);
+        expr = jexl.createExpression("duck.one");
+        result = expr.evaluate(jc);
+        assertEquals(expr.toString(), 1, result);
+        expr = jexl.createExpression("duck.user = 20");
+        result = expr.evaluate(jc);
+        assertEquals(expr.toString(), 20, result);
+        expr = jexl.createExpression("duck.user");
+        result = expr.evaluate(jc);
+        assertEquals(expr.toString(), 20, result);
+        expr = jexl.createExpression("duck.user = 'zero'");
+        result = expr.evaluate(jc);
+        expr = jexl.createExpression("duck.user");
+        result = expr.evaluate(jc);
+        assertEquals(expr.toString(), 0, result);
+    }
+
+    @SuppressWarnings("boxing")
+    public void testArray() throws Exception {
+        int[] array = { 100, 101 , 102 };
+        JexlEngine jexl = JEXL;
+        JexlContext jc = new MapContext();
+        jc.set("array", array);
+        Expression expr;
+        Object result;
+        expr = jexl.createExpression("array.1");
+        result = expr.evaluate(jc);
+        assertEquals(expr.toString(), 101, result);
+        expr = jexl.createExpression("array[1] = 1010");
+        result = expr.evaluate(jc);
+        assertEquals(expr.toString(), 1010, result);
+        expr = jexl.createExpression("array.0");
+        result = expr.evaluate(jc);
+        assertEquals(expr.toString(), 100, result);
+    }
+
+    /**
+     * Asserts that the given expression returns the given value when applied to the
+     * given context
+     */
+    protected void assertExpression(JexlContext jc, String expression, Object expected) throws Exception
+    {
+        Expression e = JEXL.createExpression(expression);
+        Object actual = e.evaluate(jc);
+        assertEquals(expression, expected, actual);
+    }
+
+}
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/JexlTestCase.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/JexlTestCase.java
new file mode 100644
index 0000000..96c88f1
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/JexlTestCase.java
@@ -0,0 +1,244 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.List;
+import java.util.ArrayList;
+
+import org.apache.commons.jexl2.parser.JexlNode;
+import org.apache.commons.jexl2.parser.ASTJexlScript;
+
+import junit.framework.TestCase;
+
+/**
+ * Implements runTest methods to dynamically instantiate and invoke a test,
+ * wrapping the call with setUp(), tearDown() calls.
+ * Eases the implementation of main methods to debug.
+ */
+public class JexlTestCase extends TestCase {
+    /** No parameters signature for test run. */
+    private static final Class<?>[] noParms = {};
+    /** String parameter signature for test run. */
+    private static final Class<?>[] stringParm = {String.class};
+
+    /** A default Jexl engine instance. */
+    protected final JexlEngine JEXL;
+
+    public JexlTestCase(String name) {
+        this(name, new JexlEngine());
+    }
+    protected JexlTestCase(String name, JexlEngine jexl) {
+        super(name);
+        JEXL = jexl;
+        JEXL.setCache(512);
+    }
+    public JexlTestCase() {
+        this(new JexlEngine());
+    }
+    protected JexlTestCase(JexlEngine jexl) {
+        super();
+        JEXL = jexl;
+        JEXL.setCache(512);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        debuggerCheck(JEXL);
+    }
+    
+    /**
+     * Will force testing the debugger for each derived test class by
+     * recreating each expression from the JexlNode in the JexlEngine cache &
+     * testing them for equality with the origin.
+     * @throws Exception
+     */
+    public static void debuggerCheck(JexlEngine jexl) throws Exception {
+        // without a cache, nothing to check
+        if (jexl.cache == null) {
+            return;
+        }
+        JexlEngine jdbg = new JexlEngine();
+        Debugger dbg = new Debugger();
+        // iterate over all expression in cache
+        Iterator<Map.Entry<String,ASTJexlScript>> inodes = jexl.cache.entrySet().iterator();
+        while (inodes.hasNext()) {
+            Map.Entry<String,ASTJexlScript> entry = inodes.next();
+            JexlNode node = entry.getValue();
+            // recreate expr string from AST
+            dbg.debug(node);
+            String expressiondbg = dbg.data();
+            // recreate expr from string
+            Expression exprdbg = jdbg.createExpression(expressiondbg);
+            // make arg cause become the root cause
+            JexlNode root = ((ExpressionImpl) exprdbg).script;
+            while (root.jjtGetParent() != null) {
+                root = root.jjtGetParent();
+            }
+            // test equality
+            String reason = JexlTestCase.checkEquals(root, node);
+            if (reason != null) {
+                throw new RuntimeException("debugger equal failed: "
+                                           + expressiondbg
+                                           +" /**** "  +reason+" **** */ "
+                                           + entry.getKey());
+            }
+        }
+    }
+
+    /**
+     * Creates a list of all descendants of a script including itself.
+     * @param script the script to flatten
+     * @return the descendants-and-self list
+     */
+    private static ArrayList<JexlNode> flatten(JexlNode node) {
+        ArrayList<JexlNode> list = new ArrayList<JexlNode>();
+        flatten(list, node);
+        return list;
+    }
+
+    /**
+     * Recursively adds all children of a script to the list of descendants.
+     * @param list the list of descendants to add to
+     * @param script the script & descendants to add
+     */
+    private static void flatten(List<JexlNode> list, JexlNode node) {
+        int nc = node.jjtGetNumChildren();
+        list.add(node);
+        for(int c = 0; c < nc; ++c) {
+            flatten(list, node.jjtGetChild(c));
+        }
+    }
+
+    /**
+     * Checks the equality of 2 nodes by comparing all their descendants.
+     * Descendants must have the same class and same image if non null.
+     * @param lhs the left script
+     * @param rhs the right script
+     * @return null if true, a reason otherwise
+     */
+    private static String checkEquals(JexlNode lhs, JexlNode rhs) {
+        if (lhs != rhs) {
+            ArrayList<JexlNode> lhsl = flatten(lhs);
+            ArrayList<JexlNode> rhsl = flatten(rhs);
+            if (lhsl.size() != rhsl.size()) {
+                 return "size: " + lhsl.size() + " != " + rhsl.size();
+            }
+            for(int n = 0; n < lhsl.size(); ++n) {
+                lhs = lhsl.get(n);
+                rhs = rhsl.get(n);
+                if (lhs.getClass() != rhs.getClass()) {
+                    return "class: " + lhs.getClass() + " != " + rhs.getClass();
+                }
+                if ((lhs.image == null && rhs.image != null)
+                    || (lhs.image != null && rhs.image == null)) {
+                    return "image: " + lhs.image + " != " + rhs.image;
+                }
+                if (lhs.image != null && !lhs.image.equals(rhs.image)) {
+                    return "image: " + lhs.image + " != " + rhs.image;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Dynamically runs a test method.
+     * @param name the test method to run
+     * @throws Exception if anything goes wrong
+     */
+    public void runTest(String name) throws Exception {
+        if ("runTest".equals(name)) {
+            return;
+        }
+        Method method = null;
+        try {
+            method = this.getClass().getDeclaredMethod(name, noParms);
+        }
+        catch(Exception xany) {
+            fail("no such test: " + name);
+            return;
+        }
+        try {
+            this.setUp();
+            method.invoke(this);
+        } finally {
+            this.tearDown();
+        }
+    }
+
+    /**
+     * Instantiate and runs a test method; useful for debugging purpose.
+     * For instance:
+     * <code>
+     * public static void main(String[] args) throws Exception {
+     *   runTest("BitwiseOperatorTest","testAndVariableNumberCoercion");
+     * }
+     * </code>
+     * @param tname the test class name
+     * @param mname the test class method
+     * @throws Exception
+     */
+    public static void runTest(String tname, String mname) throws Exception {
+        String testClassName = "org.apache.commons.jexl2."+tname;
+        Class<JexlTestCase> clazz = null;
+        JexlTestCase test = null;
+        // find the class
+        try {
+            clazz = (Class<JexlTestCase>) Class.forName(testClassName);
+        }
+        catch(ClassNotFoundException xclass) {
+            fail("no such class: " + testClassName);
+            return;
+        }
+        // find ctor & instantiate
+        Constructor<JexlTestCase> ctor = null;
+        try {
+            ctor = clazz.getConstructor(stringParm);
+            test = ctor.newInstance("debug");
+        }
+        catch(NoSuchMethodException xctor) {
+            // instantiate default class ctor
+            try {
+                test = clazz.newInstance();
+            }
+            catch(Exception xany) {
+                fail("cant instantiate test: " + xany);
+                return;
+            }
+        }
+        catch(Exception xany) {
+            fail("cant instantiate test: " + xany);
+            return;
+        }
+        // Run the test
+        test.runTest(mname);
+    }
+
+    /**
+     * Runs a test.
+     * @param args where args[0] is the test class name and args[1] the test class method
+     * @throws Exception
+     */
+    public static void main(String[] args) throws Exception {
+        runTest(args[0], args[1]);
+    }
+}
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/MapLiteralTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/MapLiteralTest.java
new file mode 100644
index 0000000..35a41d3
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/MapLiteralTest.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Tests for map literals
+ *
+ * @author Peter Royal
+ * @since 1.2
+ */
+public class MapLiteralTest extends JexlTestCase {
+
+    public void testLiteralWithStrings() throws Exception {
+        Expression e = JEXL.createExpression( "{ 'foo' : 'bar' }" );
+        JexlContext jc = new MapContext();
+
+        Object o = e.evaluate( jc );
+        assertEquals( Collections.singletonMap( "foo", "bar" ), o );
+    }
+
+    public void testLiteralWithMultipleEntries() throws Exception {
+        Expression e = JEXL.createExpression( "{ 'foo' : 'bar', 'eat' : 'food' }" );
+        JexlContext jc = new MapContext();
+
+        Map<String, String> expected = new HashMap<String, String>();
+        expected.put( "foo", "bar" );
+        expected.put( "eat", "food" );
+
+        Object o = e.evaluate( jc );
+        assertEquals( expected, o );
+    }
+
+    public void testLiteralWithNumbers() throws Exception {
+        Expression e = JEXL.createExpression( "{ 5 : 10 }" );
+        JexlContext jc = new MapContext();
+
+        Object o = e.evaluate( jc );
+        assertEquals( Collections.singletonMap( new Integer( 5 ), new Integer( 10 ) ), o );
+
+        e = JEXL.createExpression("m = { 3 : 30, 4 : 40, 5 : 'fifty', '7' : 'seven', 7 : 'SEVEN' }");
+        e.evaluate(jc);
+
+        e = JEXL.createExpression("m.3");
+        o = e.evaluate(jc);
+        assertEquals(new Integer(30), o);
+
+        e = JEXL.createExpression("m[4]");
+        o = e.evaluate(jc);
+        assertEquals(new Integer(40), o);
+
+        jc.set("i", Integer.valueOf(5));
+        e = JEXL.createExpression("m[i]");
+        o = e.evaluate(jc);
+        assertEquals("fifty", o);
+
+        e = JEXL.createExpression("m.3 = 'thirty'");
+        e.evaluate(jc);
+        e = JEXL.createExpression("m.3");
+        o = e.evaluate(jc);
+        assertEquals("thirty", o);
+
+        e = JEXL.createExpression("m['7']");
+        o = e.evaluate(jc);
+        assertEquals("seven", o);
+
+        e = JEXL.createExpression("m.7");
+        o = e.evaluate(jc);
+        assertEquals("SEVEN", o);
+
+        jc.set("k", Integer.valueOf(7));
+        e = JEXL.createExpression("m[k]");
+        o = e.evaluate(jc);
+        assertEquals("SEVEN", o);
+
+        jc.set("k", "7");
+        e = JEXL.createExpression("m[k]");
+        o = e.evaluate(jc);
+        assertEquals("seven", o);
+    }
+
+    public void testSizeOfSimpleMapLiteral() throws Exception {
+        Expression e = JEXL.createExpression( "size({ 'foo' : 'bar' })" );
+        JexlContext jc = new MapContext();
+
+        Object o = e.evaluate( jc );
+        assertEquals( new Integer( 1 ), o );
+    }
+
+    public void testCallingMethodsOnNewMapLiteral() throws Exception {
+        Expression e = JEXL.createExpression( "size({ 'foo' : 'bar' }.values())" );
+        JexlContext jc = new MapContext();
+
+        Object o = e.evaluate( jc );
+        assertEquals( new Integer( 1 ), o );
+    }
+
+    public void testNotEmptySimpleMapLiteral() throws Exception {
+        Expression e = JEXL.createExpression( "empty({ 'foo' : 'bar' })" );
+        JexlContext jc = new MapContext();
+
+        Object o = e.evaluate( jc );
+        assertFalse( ( (Boolean) o ).booleanValue() );
+    }
+
+    public void testMapMapLiteral() throws Exception {
+        Expression e = JEXL.createExpression( "{'foo' : { 'inner' : 'bar' }}" );
+        JexlContext jc = new MapContext();
+        Object o = e.evaluate( jc );
+        assertNotNull(o);
+
+        jc.set("outer", o);
+        e = JEXL.createExpression("outer.foo.inner");
+        o = e.evaluate( jc );
+        assertEquals( "bar", o );
+    }
+
+    public void testMapArrayLiteral() throws Exception {
+        Expression e = JEXL.createExpression( "{'foo' : [ 'inner' , 'bar' ]}" );
+        JexlContext jc = new MapContext();
+        Object o = e.evaluate( jc );
+        assertNotNull(o);
+
+        jc.set("outer", o);
+        e = JEXL.createExpression("outer.foo.1");
+        o = e.evaluate( jc );
+        assertEquals( "bar", o );
+    }
+
+}
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/MethodTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/MethodTest.java
new file mode 100644
index 0000000..63af1b3
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/MethodTest.java
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+import org.apache.commons.jexl2.junit.Asserter;
+
+/**
+ * Tests for calling methods on objects
+ * 
+ * @since 2.0
+ */
+public class MethodTest extends JexlTestCase {
+
+    private Asserter asserter;
+
+    private static final String METHOD_STRING = "Method string";
+
+    public static class TestClass {
+        public String testVarArgs(Integer[] args) {
+            return "Test";
+        }
+    }
+
+    public static class Functor {
+        public int ten() {
+            return 10;
+        }
+        public int plus10(int num) {
+            return num + 10;
+        }
+        public static int TWENTY() {
+            return 20;
+        }
+        public static int PLUS20(int num) {
+            return num + 20;
+        }
+        public static Class<?> NPEIfNull(Object x) {
+            return x.getClass();
+        }
+    }
+    
+    public static class EnhancedContext extends MapContext {
+        int factor = 6;
+    }
+
+    public static class ContextualFunctor {
+        private final EnhancedContext context;
+        public ContextualFunctor(EnhancedContext theContext) {
+            context = theContext;
+        }
+        public int ratio(int n) {
+            context.factor -= 1;
+            return n / context.factor;
+        }
+    }
+
+    @Override
+    public void setUp() {
+        asserter = new Asserter(JEXL);
+    }
+
+    public void testCallVarArgMethod() throws Exception {
+        asserter.setVariable("test", new TestClass());
+        asserter.assertExpression("test.testVarArgs(1,2,3,4,5)", "Test");
+    }
+
+    public void testInvoke() throws Exception {
+        Functor func = new Functor();
+        assertEquals(Integer.valueOf(10), JEXL.invokeMethod(func, "ten"));
+        assertEquals(Integer.valueOf(42), JEXL.invokeMethod(func, "PLUS20", Integer.valueOf(22)));
+        try {
+            JEXL.invokeMethod(func, "nonExistentMethod");
+            fail("method does not exist!");
+        } catch(Exception xj0) {
+            // ignore
+        }
+        try {
+            JEXL.invokeMethod(func, "NPEIfNull", (Object[]) null);
+            fail("method should have thrown!");
+        } catch(Exception xj0) {
+            // ignore
+        }
+    }
+
+    /**
+     * test a simple method expression
+     */
+    public void testMethod() throws Exception {
+        // tests a simple method expression
+        asserter.setVariable("foo", new Foo());
+        asserter.assertExpression("foo.bar()", METHOD_STRING);
+    }
+
+    public void testMulti() throws Exception {
+        asserter.setVariable("foo", new Foo());
+        asserter.assertExpression("foo.innerFoo.bar()", METHOD_STRING);
+    }
+
+    /**
+     * test some String method calls
+     */
+    public void testStringMethods() throws Exception {
+        asserter.setVariable("foo", "abcdef");
+        asserter.assertExpression("foo.substring(3)", "def");
+        asserter.assertExpression("foo.substring(0,(size(foo)-3))", "abc");
+        asserter.assertExpression("foo.substring(0,size(foo)-3)", "abc");
+        asserter.assertExpression("foo.substring(0,foo.length()-3)", "abc");
+        asserter.assertExpression("foo.substring(0, 1+1)", "ab");
+    }
+
+    /**
+     * Ensures static methods on objects can be called.
+     */
+    public void testStaticMethodInvocation() throws Exception {
+        asserter.setVariable("aBool", Boolean.FALSE);
+        asserter.assertExpression("aBool.valueOf('true')", Boolean.TRUE);
+    }
+
+    public void testStaticMethodInvocationOnClasses() throws Exception {
+        asserter.setVariable("Boolean", Boolean.class);
+        asserter.assertExpression("Boolean.valueOf('true')", Boolean.TRUE);
+    }
+
+    public static class MyMath {
+        public double cos(double x) {
+            return Math.cos(x);
+        }
+    }
+
+   public void testTopLevelCall() throws Exception {
+        java.util.Map<String, Object> funcs = new java.util.HashMap<String, Object>();
+        funcs.put(null, new Functor());
+        funcs.put("math", new MyMath());
+        funcs.put("cx", ContextualFunctor.class);
+        JEXL.setFunctions(funcs);
+
+        JexlContext jc = new EnhancedContext();
+
+        Expression e = JEXL.createExpression("ten()");
+        Object o = e.evaluate(jc);
+        assertEquals("Result is not 10", new Integer(10), o);
+
+        e = JEXL.createExpression("plus10(10)");
+        o = e.evaluate(jc);
+        assertEquals("Result is not 20", new Integer(20), o);
+
+        e = JEXL.createExpression("plus10(ten())");
+        o = e.evaluate(jc);
+        assertEquals("Result is not 20", new Integer(20), o);
+
+        jc.set("pi", new Double(Math.PI));
+        e = JEXL.createExpression("math:cos(pi)");
+        o = e.evaluate(jc);
+        assertEquals(Double.valueOf(-1),o);
+      
+        e = JEXL.createExpression("cx:ratio(10) + cx:ratio(20)");
+        o = e.evaluate(jc);
+        assertEquals(Integer.valueOf(7),o);
+    }
+
+    public void testNamespaceCall() throws Exception {
+        java.util.Map<String, Object> funcs = new java.util.HashMap<String, Object>();
+        funcs.put("func", new Functor());
+        funcs.put("FUNC", Functor.class);
+        JEXL.setFunctions(funcs);
+
+        Expression e = JEXL.createExpression("func:ten()");
+        JexlContext jc = new MapContext();
+        Object o = e.evaluate(jc);
+        assertEquals("Result is not 10", new Integer(10), o);
+
+        e = JEXL.createExpression("func:plus10(10)");
+        jc = new MapContext();
+        o = e.evaluate(jc);
+        assertEquals("Result is not 20", new Integer(20), o);
+
+        e = JEXL.createExpression("func:plus10(func:ten())");
+        jc = new MapContext();
+        o = e.evaluate(jc);
+        assertEquals("Result is not 20", new Integer(20), o);
+
+        e = JEXL.createExpression("FUNC:PLUS20(10)");
+        jc = new MapContext();
+        o = e.evaluate(jc);
+        assertEquals("Result is not 30", new Integer(30), o);
+
+        e = JEXL.createExpression("FUNC:PLUS20(FUNC:TWENTY())");
+        jc = new MapContext();
+        o = e.evaluate(jc);
+        assertEquals("Result is not 40", new Integer(40), o);
+    }
+
+}
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/ParseFailuresTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/ParseFailuresTest.java
new file mode 100644
index 0000000..0a3c41b
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/ParseFailuresTest.java
@@ -0,0 +1,100 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+/**
+ * Tests for malformed expressions and scripts.
+ * ({@link org.apache.commons.jexl2.JexlEngine#createExpression(String)} and
+ * {@link org.apache.commons.jexl2.JexlEngine#createScript(String)} should throw
+ * {@link org.apache.commons.jexl2.JexlException}s).
+ *
+ * @since 1.1
+ */
+public class ParseFailuresTest extends JexlTestCase {
+
+    /**
+     * Create the test.
+     *
+     * @param testName name of the test
+     */
+    public ParseFailuresTest(String testName) {
+        super(testName);
+        JEXL.setSilent(false);
+    }
+
+    public void testMalformedExpression1() throws Exception {
+        // this will throw a JexlException
+        String badExpression = "eq";
+        try {
+            JEXL.createExpression(badExpression);
+            fail("Parsing \"" + badExpression
+                + "\" should result in a JexlException");
+        } catch (JexlException pe) {
+            // expected
+        }
+    }
+
+    public void testMalformedExpression2() throws Exception {
+        // this will throw a TokenMgrErr, which we rethrow as a JexlException
+        String badExpression = "?";
+        try {
+            JEXL.createExpression(badExpression);
+            fail("Parsing \"" + badExpression
+                + "\" should result in a JexlException");
+        } catch (JexlException pe) {
+            // expected
+        }
+    }
+
+    public void testMalformedScript1() throws Exception {
+        // this will throw a TokenMgrErr, which we rethrow as a JexlException
+        String badScript = "eq";
+        try {
+            JEXL.createScript(badScript);
+            fail("Parsing \"" + badScript
+                + "\" should result in a JexlException");
+        } catch (JexlException pe) {
+            // expected
+        }
+    }
+
+
+    public void testMalformedScript2() throws Exception {
+        // this will throw a TokenMgrErr, which we rethrow as a JexlException
+        String badScript = "?";
+        try {
+            JEXL.createScript(badScript);
+            fail("Parsing \"" + badScript
+                + "\" should result in a JexlException");
+        } catch (JexlException pe) {
+            // expected
+        }
+    }
+
+    public void testMalformedScript3() throws Exception {
+        // this will throw a TokenMgrErr, which we rethrow as a JexlException
+        String badScript = "foo=1;bar=2;a?b:c:d;";
+        try {
+            JEXL.createScript(badScript);
+            fail("Parsing \"" + badScript
+                + "\" should result in a JexlException");
+        } catch (JexlException pe) {
+            // expected
+        }
+    }
+
+}
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/PublicFieldsTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/PublicFieldsTest.java
new file mode 100644
index 0000000..6b6ed26
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/PublicFieldsTest.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+import org.apache.commons.jexl2.introspection.UberspectImpl;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Tests public field set/get.
+ */
+public class PublicFieldsTest extends JexlTestCase {
+    // some constants
+    private static final String LOWER42 = "fourty-two";
+    private static final String UPPER42 = "FOURTY-TWO";
+    /**
+     * An Inner class.
+     */
+    public static class Inner {
+        public double aDouble = 42.0;
+    }
+
+    /**
+     * A Struct, all fields public
+     */
+    public static class Struct {
+        public Inner inner = new Inner();
+        public int anInt = 42;
+        public String aString = LOWER42;
+    }
+
+    // a pub instance
+    private Struct pub;
+    // the JexlContext to use
+    private JexlContext ctxt;
+
+    public PublicFieldsTest() {
+        JEXL.setLenient(false);
+    }
+
+    @Override
+    public void setUp() {
+        pub = new Struct();
+        ctxt = new MapContext();
+        ctxt.set("pub", pub);
+    }
+
+    public void testGetInt() throws Exception {
+        Expression get = JEXL.createExpression("pub.anInt");
+        assertEquals(42, get.evaluate(ctxt));
+        JEXL.setProperty(pub, "anInt", -42);
+        assertEquals(-42, get.evaluate(ctxt));
+    }
+
+    public void testSetInt() throws Exception {
+        Expression set = JEXL.createExpression("pub.anInt = value");
+        ctxt.set("value", -42);
+        assertEquals(-42, set.evaluate(ctxt));
+        assertEquals(-42, JEXL.getProperty(pub, "anInt"));
+        ctxt.set("value", 42);
+        assertEquals(42, set.evaluate(ctxt));
+        assertEquals(42, JEXL.getProperty(pub, "anInt"));
+        try {
+            ctxt.set("value", UPPER42);
+            assertEquals(null, set.evaluate(ctxt));
+            fail("should have thrown");
+        } catch(JexlException xjexl) {}
+    }
+
+    public void testGetString() throws Exception {
+        Expression get = JEXL.createExpression("pub.aString");
+        assertEquals(LOWER42, get.evaluate(ctxt));
+        JEXL.setProperty(pub, "aString", UPPER42);
+        assertEquals(UPPER42, get.evaluate(ctxt));
+    }
+
+    public void testSetString() throws Exception {
+        Expression set = JEXL.createExpression("pub.aString = value");
+        ctxt.set("value", UPPER42);
+        assertEquals(UPPER42, set.evaluate(ctxt));
+        assertEquals(UPPER42, JEXL.getProperty(pub, "aString"));
+        ctxt.set("value", LOWER42);
+        assertEquals(LOWER42, set.evaluate(ctxt));
+        assertEquals(LOWER42, JEXL.getProperty(pub, "aString"));
+    }
+
+    public void testGetInnerDouble() throws Exception {
+        Expression get = JEXL.createExpression("pub.inner.aDouble");
+        assertEquals(42.0, get.evaluate(ctxt));
+        JEXL.setProperty(pub, "inner.aDouble", -42);
+        assertEquals(-42.0, get.evaluate(ctxt));
+    }
+
+    public void testSetInnerDouble() throws Exception {
+        Expression set = JEXL.createExpression("pub.inner.aDouble = value");
+        ctxt.set("value", -42.0);
+        assertEquals(-42.0, set.evaluate(ctxt));
+        assertEquals(-42.0, JEXL.getProperty(pub, "inner.aDouble"));
+        ctxt.set("value", 42.0);
+        assertEquals(42.0, set.evaluate(ctxt));
+        assertEquals(42.0, JEXL.getProperty(pub, "inner.aDouble"));
+        try {
+            ctxt.set("value", UPPER42);
+            assertEquals(null, set.evaluate(ctxt));
+            fail("should have thrown");
+        } catch(JexlException xjexl) {}
+    }
+
+}
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/ScriptTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/ScriptTest.java
new file mode 100644
index 0000000..aa8ba18
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/ScriptTest.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+
+import java.io.File;
+import java.net.URL;
+
+/**
+ * Tests for Script
+ * @since 1.1
+ */
+public class ScriptTest extends JexlTestCase {
+    static final String TEST1 =  "src/test/scripts/test1.jexl";
+
+    // test class for testScriptUpdatesContext
+    // making this class private static will cause the test to fail.
+    // this is due to unusual code in ClassMap.getAccessibleMethods(Class)
+    // that treats non-public classes in a specific way. Why getAccessibleMethods
+    // does this is not known yet.
+    public static class Tester {
+        private String code;
+        public String getCode () { 
+            return code; 
+        }
+        public void setCode(String c) {
+            code = c;
+        }
+    }
+    /**
+     * Create a new test case.
+     * @param name case name
+     */
+    public ScriptTest(String name) {
+        super(name);
+    }
+
+    /**
+     * Test creating a script from a string.
+     */
+    public void testSimpleScript() throws Exception {
+        String code = "while (x < 10) x = x + 1;";
+        Script s = JEXL.createScript(code);
+        JexlContext jc = new MapContext();
+        jc.set("x", new Integer(1));
+    
+        Object o = s.execute(jc);
+        assertEquals("Result is wrong", new Integer(10), o);
+        assertEquals("getText is wrong", code, s.getText());
+    }
+
+    public void testScriptFromFile() throws Exception {
+        File testScript = new File(TEST1);
+        Script s = JEXL.createScript(testScript);
+        JexlContext jc = new MapContext();
+        jc.set("out", System.out);
+        Object result = s.execute(jc);
+        assertNotNull("No result", result);
+        assertEquals("Wrong result", new Integer(7), result);
+    }
+
+    public void testScriptFromURL() throws Exception {
+        URL testUrl = new File("src/test/scripts/test1.jexl").toURI().toURL();
+        Script s = JEXL.createScript(testUrl);
+        JexlContext jc = new MapContext();
+        jc.set("out", System.out);
+        Object result = s.execute(jc);
+        assertNotNull("No result", result);
+        assertEquals("Wrong result", new Integer(7), result);
+    }
+
+    public void testScriptUpdatesContext() throws Exception {
+        String jexlCode = "resultat.setCode('OK')";
+        Expression e = JEXL.createExpression(jexlCode);
+        Script s = JEXL.createScript(jexlCode);
+
+        Tester resultatJexl = new Tester();
+        JexlContext jc = new MapContext();
+        jc.set("resultat", resultatJexl);
+
+        resultatJexl.setCode("");
+        e.evaluate(jc);
+        assertEquals("OK", resultatJexl.getCode());
+        resultatJexl.setCode("");
+        s.execute(jc);
+        assertEquals("OK", resultatJexl.getCode());
+    }
+
+}
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/UnifiedJEXLTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/UnifiedJEXLTest.java
new file mode 100644
index 0000000..42ddeb3
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/UnifiedJEXLTest.java
@@ -0,0 +1,241 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+/**
+ * Test cases for the UnifiedEL.
+ */
+public class UnifiedJEXLTest extends JexlTestCase {
+    private static final JexlEngine ENGINE = new JexlEngine();
+    static {
+        ENGINE.setLenient(false);
+        ENGINE.setSilent(false);
+        ENGINE.setCache(128);
+    }
+    private static final UnifiedJEXL EL = new UnifiedJEXL(ENGINE);
+    private static final Log LOG = LogFactory.getLog(UnifiedJEXL.class);
+    private JexlContext context = null;
+    private Map<String,Object> vars =null;
+
+    @Override
+    public void setUp() throws Exception {
+        // ensure jul logging is only error
+        java.util.logging.Logger.getLogger(JexlEngine.class.getName()).setLevel(java.util.logging.Level.SEVERE);
+        vars = new HashMap<String,Object>();
+        context = new MapContext(vars);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        debuggerCheck(ENGINE);
+        super.tearDown();
+    }
+
+    public static class Froboz {
+        int value;
+        public Froboz(int v) {
+            value = v;
+        }
+        public void setValue(int v) {
+            value = v;
+        }
+        public int getValue() {
+            return value;
+        }
+        public int plus10() {
+            int i = value;
+            value += 10;
+            return i;
+        }
+    }
+
+    public UnifiedJEXLTest(String testName) {
+        super(testName);
+    }
+
+    public void testStatement() throws Exception {
+        vars.put("froboz", new Froboz(123));
+        UnifiedJEXL.Expression check = EL.parse("${froboz.value = 32; froboz.plus10(); froboz.value}");
+        Object o = check.evaluate(context);
+        assertEquals("Result is not 42", new Integer(42), o);
+    }
+
+    public void testAssign() throws Exception {
+        UnifiedJEXL.Expression assign = EL.parse("${froboz.value = 10}");
+        UnifiedJEXL.Expression check = EL.parse("${froboz.value}");
+        Object o = assign.evaluate(context);
+        assertEquals("Result is not 10", new Integer(10), o);
+        o = check.evaluate(context);
+        assertEquals("Result is not 10", new Integer(10), o);
+    }
+    
+    public void testComposite() throws Exception {
+        UnifiedJEXL.Expression expr = EL.parse("Dear ${p} ${name};");
+        vars.put("p", "Mr");
+        vars.put("name", "Doe");
+        assertTrue("expression should be immediate", expr.isImmediate());
+        Object o = expr.evaluate(context);
+        assertEquals("Dear Mr Doe;", o);
+        vars.put("p", "Ms");
+        vars.put("name", "Jones");
+        o = expr.evaluate(context);
+        assertEquals("Dear Ms Jones;", o);
+    }
+
+    public void testPrepareEvaluate() throws Exception {
+        UnifiedJEXL.Expression expr = EL.parse("Dear #{p} ${name};");
+        assertTrue("expression should be deferred", expr.isDeferred());
+        vars.put("name", "Doe");
+        UnifiedJEXL.Expression  phase1 = expr.prepare(context);
+        String as = phase1.asString();
+        assertEquals("Dear #{p} Doe;", as);
+        vars.put("p", "Mr");
+        vars.put("name", "Should not be used in 2nd phase");
+        Object o = phase1.evaluate(context);
+        assertEquals("Dear Mr Doe;", o);
+    }
+    
+    public void testNested() throws Exception {
+        UnifiedJEXL.Expression expr = EL.parse("#{${hi}+'.world'}");
+        vars.put("hi", "hello");
+        vars.put("hello.world", "Hello World!");
+        Object o = expr.evaluate(context);
+        assertTrue("source should not be expression", expr.getSource() != expr.prepare(context));
+        assertTrue("expression should be deferred", expr.isDeferred());
+        assertEquals("Hello World!", o);
+    }
+
+    public void testImmediate() throws Exception {
+        JexlContext none = null;
+        UnifiedJEXL.Expression expr = EL.parse("${'Hello ' + 'World!'}");
+        assertTrue("prepare should return same expression", expr.prepare(none) == expr);
+        Object o = expr.evaluate(none);
+        assertTrue("expression should be immediate", expr.isImmediate());
+        assertEquals("Hello World!", o);
+    }
+
+    public void testConstant() throws Exception {
+        JexlContext none = null;
+        UnifiedJEXL.Expression expr = EL.parse("Hello World!");
+        assertTrue("prepare should return same expression", expr.prepare(none) == expr);
+        Object o = expr.evaluate(none);
+        assertTrue("expression should be immediate", expr.isImmediate());
+        assertEquals("Hello World!", o);
+    }
+
+    public void testDeferred() throws Exception {
+        JexlContext none = null;
+        UnifiedJEXL.Expression expr = EL.parse("#{'world'}");
+        assertTrue("prepare should return same expression", expr.prepare(none) == expr);
+        Object o = expr.evaluate(none);
+        assertTrue("expression should be deferred", expr.isDeferred());
+        assertEquals("world", o);
+    }
+
+    public void testEscape() throws Exception {
+        JexlContext none = null;
+        UnifiedJEXL.Expression expr;
+        Object o;
+        // $ and # are escapable in UnifiedJEXL
+        expr = EL.parse("\\#{'world'}");
+        o = expr.evaluate(none);
+        assertEquals("#{'world'}", o);
+        expr = EL.parse("\\${'world'}");
+        o = expr.evaluate(none);
+        assertEquals("${'world'}", o);
+    }
+
+    public void testEscapeString() throws Exception {
+        UnifiedJEXL.Expression expr = EL.parse("\\\"${'world\\'s finest'}\\\"");
+        JexlContext none = null;
+        Object o = expr.evaluate(none);
+        assertEquals("\"world's finest\"", o);
+    }
+
+    public void testNonEscapeString() throws Exception {
+        UnifiedJEXL.Expression expr = EL.parse("c:\\some\\windows\\path");
+        JexlContext none = null;
+        Object o = expr.evaluate(none);
+        assertEquals("c:\\some\\windows\\path", o);
+    }
+
+    public void testMalformed() throws Exception {
+        try {
+            UnifiedJEXL.Expression expr = EL.parse("${'world'");
+            JexlContext none = null;
+            expr.evaluate(none);
+            fail("should be malformed");
+        }
+        catch(UnifiedJEXL.Exception xjexl) {
+            // expected
+            String xmsg = xjexl.getMessage();
+            LOG.warn(xmsg);
+        }
+    }
+    
+    public void testMalformedNested() throws Exception {
+        try {
+            UnifiedJEXL.Expression expr = EL.parse("#{${hi} world}");
+            JexlContext none = null;
+            expr.evaluate(none);
+            fail("should be malformed");
+        }
+        catch(UnifiedJEXL.Exception xjexl) {
+            // expected
+            String xmsg = xjexl.getMessage();
+            LOG.warn(xmsg);
+        }
+    }
+    
+    public void testBadContextNested() throws Exception {
+        try {
+            UnifiedJEXL.Expression expr = EL.parse("#{${hi}+'.world'}");
+            JexlContext none = null;
+            expr.evaluate(none);
+            fail("should be malformed");
+        }
+        catch(UnifiedJEXL.Exception xjexl) {
+            // expected
+            String xmsg = xjexl.getMessage();
+            LOG.warn(xmsg);
+        }
+    }
+    
+    public void testCharAtBug() throws Exception {
+        vars.put("foo", "abcdef");
+        UnifiedJEXL.Expression expr = EL.parse("${foo.substring(2,4)/*comment*/}");
+        Object o = expr.evaluate(context);
+        assertEquals("cd", o);
+        
+        vars.put("bar", "foo");
+        try {
+            ENGINE.setSilent(true);
+            expr = EL.parse("#{${bar}+'.charAt(-2)'}");
+            expr = expr.prepare(context);
+            o = expr.evaluate(context);
+            assertEquals(null, o);
+        }
+        finally {
+            ENGINE.setSilent(false);
+        }
+
+    }
+
+}
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/WhileTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/WhileTest.java
new file mode 100644
index 0000000..2540f4d
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/WhileTest.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2;
+
+/**
+ * Tests for while statement.
+ * @author Dion Gillard
+ * @since 1.1
+ */
+public class WhileTest extends JexlTestCase {
+
+    public WhileTest(String testName) {
+        super(testName);
+    }
+
+    public void testSimpleWhileFalse() throws Exception {
+        Expression e = JEXL.createExpression("while (false) ;");
+        JexlContext jc = new MapContext();
+
+        Object o = e.evaluate(jc);
+        assertNull("Result is not null", o);
+    }
+    
+    public void testWhileExecutesExpressionWhenLooping() throws Exception {
+        Expression e = JEXL.createExpression("while (x < 10) x = x + 1;");
+        JexlContext jc = new MapContext();
+        jc.set("x", new Integer(1));
+
+        Object o = e.evaluate(jc);
+        assertEquals("Result is wrong", new Integer(10), o);
+    }
+
+    public void testWhileWithBlock() throws Exception {
+        Expression e = JEXL.createExpression("while (x < 10) { x = x + 1; y = y * 2; }");
+        JexlContext jc = new MapContext();
+        jc.set("x", new Integer(1));
+        jc.set("y", new Integer(1));
+
+        Object o = e.evaluate(jc);
+        assertEquals("Result is wrong", new Integer(512), o);
+        assertEquals("x is wrong", new Integer(10), jc.get("x"));
+        assertEquals("y is wrong", new Integer(512), jc.get("y"));
+    }
+}
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/examples/ArrayTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/examples/ArrayTest.java
new file mode 100644
index 0000000..03085a0
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/examples/ArrayTest.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2.examples;
+
+import org.apache.commons.jexl2.*;
+import junit.framework.TestCase;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ *  Simple example to show how to access arrays.
+ *
+ *  @since 1.0
+ *  @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
+ *  @version $Id$
+ */
+public class ArrayTest extends TestCase {
+    /**
+     * An example for array access.
+     */
+    static void example(Output out) throws Exception {
+        /**
+         * First step is to retrieve an instance of a JexlEngine;
+         * it might be already existing and shared or created anew.
+         */
+        JexlEngine jexl = new JexlEngine();
+        /*
+         *  Second make a jexlContext and put stuff in it
+         */
+        JexlContext jc = new MapContext();
+
+        List<Object> l = new ArrayList<Object>();
+        l.add("Hello from location 0");
+        Integer two = new Integer(2);
+        l.add(two);
+        jc.set("array", l);
+
+        Expression e = jexl.createExpression("array[1]");
+        Object o = e.evaluate(jc);
+        out.print("Object @ location 1 = ", o, two);
+
+        e = jexl.createExpression("array[0].length()");
+        o = e.evaluate(jc);
+
+        out.print("The length of the string at location 0 is : ", o, Integer.valueOf(21));
+    }
+
+    /**
+     * Unit test entry point.
+     * @throws Exception
+     */
+    public void testExample() throws Exception {
+        example(Output.JUNIT);
+    }
+
+    /** 
+     * Command line entry point.
+     * @param args command line arguments
+     * @throws Exception cos jexl does. 
+     */
+    public static void main(String[] args) throws Exception {
+        example(Output.SYSTEM);
+    }
+}
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/examples/MethodPropertyTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/examples/MethodPropertyTest.java
new file mode 100644
index 0000000..bd21fdd
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/examples/MethodPropertyTest.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2.examples;
+
+import org.apache.commons.jexl2.*;
+import junit.framework.TestCase;
+
+/**
+ *  Simple example to show how to access method and properties.
+ *
+ *  @since 1.0
+ *  @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
+ *  @version $Id$
+ */
+public class MethodPropertyTest extends TestCase {
+    /**
+     * An example for method access.
+     */
+    public static void example(final Output out) throws Exception {
+        /**
+         * First step is to retrieve an instance of a JexlEngine;
+         * it might be already existing and shared or created anew.
+         */
+        JexlEngine jexl = new JexlEngine();
+        /*
+         *  Second make a jexlContext and put stuff in it
+         */
+        JexlContext jc = new MapContext();
+
+        /**
+         * The Java equivalents of foo and number for comparison and checking
+         */
+        Foo foo = new Foo();
+        Integer number = new Integer(10);
+
+        jc.set("foo", foo);
+        jc.set("number", number);
+
+        /*
+         *  access a method w/o args
+         */
+        Expression e = jexl.createExpression("foo.getFoo()");
+        Object o = e.evaluate(jc);
+        out.print("value returned by the method getFoo() is : ", o, foo.getFoo());
+
+        /*
+         *  access a method w/ args
+         */
+        e = jexl.createExpression("foo.convert(1)");
+        o = e.evaluate(jc);
+        out.print("value of " + e.getExpression() + " is : ", o, foo.convert(1));
+
+        e = jexl.createExpression("foo.convert(1+7)");
+        o = e.evaluate(jc);
+        out.print("value of " + e.getExpression() + " is : ", o, foo.convert(1+7));
+
+        e = jexl.createExpression("foo.convert(1+number)");
+        o = e.evaluate(jc);
+        out.print("value of " + e.getExpression() + " is : ", o, foo.convert(1+number.intValue()));
+
+        /*
+         * access a property
+         */
+        e = jexl.createExpression("foo.bar");
+        o = e.evaluate(jc);
+        out.print("value returned for the property 'bar' is : ", o, foo.get("bar"));
+
+    }
+
+    /**
+     * Helper example class.
+     */
+    public static class Foo {
+        /**
+         * Gets foo.
+         * @return a string.
+         */
+        public String getFoo() {
+            return "This is from getFoo()";
+        }
+
+        /**
+         * Gets an arbitrary property.
+         * @param arg property name.
+         * @return arg prefixed with 'This is the property '.
+         */
+        public String get(String arg) {
+            return "This is the property " + arg;
+        }
+
+        /**
+         * Gets a string from the argument.
+         * @param i a long.
+         * @return The argument prefixed with 'The value is : '
+         */
+        public String convert(long i) {
+            return "The value is : " + i;
+        }
+    }
+
+
+    /**
+     * Unit test entry point.
+     * @throws Exception
+     */
+    public void testExample() throws Exception {
+        example(Output.JUNIT);
+    }
+
+    /**
+     * Command line entry point.
+     * @param args command line arguments
+     * @throws Exception cos jexl does.
+     */
+    public static void main(String[] args) throws Exception {
+        example(Output.SYSTEM);
+    }
+}
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/examples/Output.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/examples/Output.java
new file mode 100644
index 0000000..c9553f0
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/examples/Output.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2.examples;
+import junit.framework.TestCase;
+
+/**
+ * Abstracts using a test within Junit or through a main method.
+ */
+public abstract class Output {
+    /**
+     * Creates an output using System.out.
+     */
+    private Output() {
+        // nothing to do
+    }
+
+    /**
+     * Outputs the actual and value or checks the actual equals the expected value.
+     * @param expr the message to output
+     * @param actual the actual value to output
+     * @param expected the expected value
+     */
+    public abstract void print(String expr, Object actual, Object expected);
+
+    /**
+     * The output instance for Junit TestCase calling assertEquals.
+     */
+    public static final Output JUNIT = new Output() {
+        @Override
+        public void print(String expr, Object actual, Object expected) {
+            TestCase.assertEquals(expr, expected, actual);
+        }
+    };
+
+        
+    /**
+     * The output instance for the general outputing to System.out.
+     */
+    public static final Output SYSTEM = new Output() {
+        @Override
+        public void print(String expr, Object actual, Object expected) {
+            System.out.print(expr);
+            System.out.println(actual);
+        }
+    };
+}
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/internal/introspection/DiscoveryTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/internal/introspection/DiscoveryTest.java
new file mode 100644
index 0000000..252f72e
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/internal/introspection/DiscoveryTest.java
@@ -0,0 +1,221 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2.internal.introspection;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+import org.apache.commons.jexl2.JexlEngine;
+import org.apache.commons.jexl2.JexlTestCase;
+import org.apache.commons.jexl2.introspection.Uberspect;
+
+import org.apache.commons.jexl2.internal.Introspector;
+import org.apache.commons.jexl2.internal.AbstractExecutor;
+import org.apache.commons.jexl2.internal.PropertyGetExecutor;
+import org.apache.commons.jexl2.internal.PropertySetExecutor;
+import org.apache.commons.jexl2.internal.DuckGetExecutor;
+import org.apache.commons.jexl2.internal.DuckSetExecutor;
+import org.apache.commons.jexl2.internal.ListGetExecutor;
+import org.apache.commons.jexl2.internal.ListSetExecutor;
+import org.apache.commons.jexl2.internal.MapGetExecutor;
+import org.apache.commons.jexl2.internal.MapSetExecutor;
+
+/**
+ * Tests for checking introspection discovery.
+ * 
+ * @since 2.0
+ */
+public class DiscoveryTest extends JexlTestCase {
+
+    public static class Duck {
+        private String value;
+        private String eulav;
+        public Duck(String v, String e) {
+            value = v;
+            eulav = e;
+        }
+        public String get(String prop) {
+            if ("value".equals(prop)) {
+                return value;
+            }
+            if ("eulav".equals(prop)) {
+                return eulav;
+            }
+            return "no such property";
+        }
+        public void set(String prop, String v) {
+            if ("value".equals(prop)) {
+                value = v;
+            } else if ("eulav".equals(prop)) {
+                eulav = v;
+            }
+        }
+    }
+
+    public static class Bean {
+        private String value;
+        private String eulav;
+        private boolean flag;
+        public Bean(String v, String e) {
+            value = v;
+            eulav = e;
+            flag = true;
+        }
+        public String getValue() {
+            return value;
+        }
+        public void setValue(String v) {
+            value = v;
+        }
+        public String getEulav() {
+            return eulav;
+        }
+        public void setEulav(String v) {
+            eulav = v;
+        }
+        public boolean isFlag() {
+            return flag;
+        }
+        public void setFlag(boolean f) {
+            flag = f;
+        }
+    }
+
+
+    public void testBeanIntrospection() throws Exception {
+        Uberspect uber = JexlEngine.getUberspect(null);
+        Introspector intro = (Introspector) uber;
+        Bean bean = new Bean("JEXL", "LXEJ");
+
+        AbstractExecutor.Get get = intro.getGetExecutor(bean, "value");
+        AbstractExecutor.Set set  = intro.getSetExecutor(bean, "value", "foo");
+        assertTrue("bean property getter", get instanceof PropertyGetExecutor);
+        assertTrue("bean property setter", set instanceof PropertySetExecutor);
+        // introspector and uberspect should return same result
+        assertEquals(get, uber.getPropertyGet(bean, "value", null));
+        assertEquals(set, uber.getPropertySet(bean, "value", "foo", null));
+        // different property should return different setter/getter
+        assertFalse(get.equals(intro.getGetExecutor(bean, "eulav")));
+        assertFalse(set.equals(intro.getSetExecutor(bean, "eulav", "foo")));
+        // setter returns argument
+        Object bar = set.execute(bean, "bar");
+        assertEquals("bar", bar);
+        // getter should return last value
+        assertEquals("bar", get.execute(bean));
+        // tryExecute should succeed on same property
+        Object quux = set.tryExecute(bean, "value", "quux");
+        assertEquals("quux", quux);
+        assertEquals("quux", get.execute(bean));
+        // tryExecute should fail on different property
+        assertEquals(AbstractExecutor.TRY_FAILED, set.tryExecute(bean, "eulav", "nope"));
+
+    }
+
+    public void testDuckIntrospection() throws Exception {
+        Uberspect uber = JexlEngine.getUberspect(null);
+        Introspector intro = (Introspector) uber;
+        Duck duck = new Duck("JEXL", "LXEJ");
+
+        AbstractExecutor.Get get = intro.getGetExecutor(duck, "value");
+        AbstractExecutor.Set set  = intro.getSetExecutor(duck, "value", "foo");
+        assertTrue("duck property getter", get instanceof DuckGetExecutor);
+        assertTrue("duck property setter", set instanceof DuckSetExecutor);
+        // introspector and uberspect should return same result
+        assertEquals(get, uber.getPropertyGet(duck, "value", null));
+        assertEquals(set, uber.getPropertySet(duck, "value", "foo", null));
+        // different property should return different setter/getter
+        assertFalse(get.equals(intro.getGetExecutor(duck, "eulav")));
+        assertFalse(set.equals(intro.getSetExecutor(duck, "eulav", "foo")));
+        // setter returns argument
+        Object bar = set.execute(duck, "bar");
+        assertEquals("bar", bar);
+        // getter should return last value
+        assertEquals("bar", get.execute(duck));
+        // tryExecute should succeed on same property
+        Object quux = set.tryExecute(duck, "value", "quux");
+        assertEquals("quux", quux);
+        assertEquals("quux", get.execute(duck));
+        // tryExecute should fail on different property
+        assertEquals(AbstractExecutor.TRY_FAILED, set.tryExecute(duck, "eulav", "nope"));
+    }
+
+    public void testListIntrospection() throws Exception {
+        Uberspect uber = JexlEngine.getUberspect(null);
+        Introspector intro = (Introspector) uber;
+        List<Object> list = new ArrayList<Object>();
+        list.add("LIST");
+        list.add("TSIL");
+
+        AbstractExecutor.Get get = intro.getGetExecutor(list, Integer.valueOf(1));
+        AbstractExecutor.Set set  = intro.getSetExecutor(list, Integer.valueOf(1), "foo");
+        assertTrue("list property getter", get instanceof ListGetExecutor);
+        assertTrue("list property setter", set instanceof ListSetExecutor);
+        // introspector and uberspect should return same result
+        assertEquals(get, uber.getPropertyGet(list, Integer.valueOf(1), null));
+        assertEquals(set, uber.getPropertySet(list, Integer.valueOf(1), "foo", null));
+        // different property should return different setter/getter
+        assertFalse(get.equals(intro.getGetExecutor(list, Integer.valueOf(0))));
+        assertFalse(get.equals(intro.getSetExecutor(list, Integer.valueOf(0), "foo")));
+        // setter returns argument
+        Object bar = set.execute(list, "bar");
+        assertEquals("bar", bar);
+        // getter should return last value
+        assertEquals("bar", get.execute(list));
+        // tryExecute should succeed on integer property
+        Object quux = set.tryExecute(list, Integer.valueOf(1), "quux");
+        assertEquals("quux", quux);
+        // getter should return last value
+        assertEquals("quux", get.execute(list));
+        // tryExecute should fail on non-integer property class
+        assertEquals(AbstractExecutor.TRY_FAILED, set.tryExecute(list, "eulav", "nope"));
+    }
+
+    public void testMapIntrospection() throws Exception {
+        Uberspect uber = JexlEngine.getUberspect(null);
+        Introspector intro = (Introspector) uber;
+        Map<String, Object> map = new HashMap<String, Object>();
+        map.put("value", "MAP");
+        map.put("eulav", "PAM");
+
+        AbstractExecutor.Get get = intro.getGetExecutor(map, "value");
+        AbstractExecutor.Set set  = intro.getSetExecutor(map, "value", "foo");
+        assertTrue("map property getter", get instanceof MapGetExecutor);
+        assertTrue("map property setter", set instanceof MapSetExecutor);
+        // introspector and uberspect should return same result
+        assertEquals(get, uber.getPropertyGet(map, "value", null));
+        assertEquals(set, uber.getPropertySet(map, "value", "foo", null));
+        // different property should return different setter/getter
+        assertFalse(get.equals(intro.getGetExecutor(map, "eulav")));
+        assertFalse(get.equals(intro.getSetExecutor(map, "eulav", "foo")));
+        // setter returns argument
+        Object bar = set.execute(map, "bar");
+        assertEquals("bar", bar);
+        // getter should return last value
+        assertEquals("bar", get.execute(map));
+        // tryExecute should succeed on same property class
+        Object quux = set.tryExecute(map, "value", "quux");
+        assertEquals("quux", quux);
+        // getter should return last value
+        assertEquals("quux", get.execute(map));
+        // tryExecute should fail on different property class
+        assertEquals(AbstractExecutor.TRY_FAILED, set.tryExecute(map, Integer.valueOf(1), "nope"));
+    }
+
+}
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/internal/introspection/MethodKeyTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/internal/introspection/MethodKeyTest.java
new file mode 100644
index 0000000..545c307
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/internal/introspection/MethodKeyTest.java
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2.internal.introspection;
+import org.apache.commons.jexl2.internal.introspection.MethodKey;
+import org.apache.commons.jexl2.internal.introspection.ClassMap;
+import junit.framework.TestCase;
+/**
+ * Checks the CacheMap.MethodKey implementation
+ */
+public class MethodKeyTest extends TestCase {
+    // A set of classes (most of them primitives)
+    private static final Class<?>[] PRIMS = {
+        Boolean.TYPE,
+        Byte.TYPE,
+        Character.TYPE,
+        Double.TYPE,
+        Float.TYPE,
+        Integer.TYPE,
+        Long.TYPE,
+        Short.TYPE,
+        String.class,
+        java.util.Date.class
+    };
+    
+    // A set of instances corresponding to the classes
+    private static final Object[] ARGS = {
+        new Boolean(true),
+        new Byte((byte) 1),
+        new Character('2'),
+        new Double(4d),
+        new Float(8f),
+        new Integer(16),
+        new Long(32l),
+        new Short((short)64),
+        "foobar",
+        new java.util.Date()
+    };
+    
+    // A set of (pseudo) method names
+    private static final String[] METHODS = {
+        "plus",
+        "minus",
+        "execute",
+        "activate",
+        "perform",
+        "apply",
+        "invoke",
+        "executeAction",
+        "activateAction",
+        "performAction",
+        "applyAction",
+        "invokeAction",
+        "executeFunctor",
+        "activateFunctor",
+        "performFunctor",
+        "applyFunctor",
+        "invokeFunctor",
+        "executeIt",
+        "activateIt",
+        "performIt",
+        "applyIt",
+        "invokeIt"
+    };
+    
+    /** from key to string */
+    private static final java.util.Map< MethodKey, String> byKey;
+    /** form string to key */
+    private static final java.util.Map<String,MethodKey> byString;
+    /** the list of keys we generated & test against */
+    private static final MethodKey[] keyList;
+    
+    /** Creates & inserts a key into the byKey & byString map */
+    private static void setUpKey(String name, Class<?>[] parms) {
+        MethodKey key = new MethodKey(name, parms);
+        String str = key.toString();
+        byKey.put(key, str);
+        byString.put(str, key);
+        
+    }
+
+    /** Generate a list of method*(prims*), method(prims*, prims*), method*(prims*,prims*,prims*) */
+    static {
+        byKey = new java.util.HashMap< MethodKey, String>();
+        byString = new java.util.HashMap<String,MethodKey>();
+        for (int m = 0; m < METHODS.length; ++m) {
+            String method = METHODS[m];
+            for (int p0 = 0; p0 < PRIMS.length; ++p0) {
+                Class<?>[] arg0 = {PRIMS[p0]};
+                setUpKey(method, arg0);
+                for (int p1 = 0; p1 < PRIMS.length; ++p1) {
+                    Class<?>[] arg1 = {PRIMS[p0], PRIMS[p1]};
+                    setUpKey(method, arg1);
+                    for (int p2 = 0; p2 < PRIMS.length; ++p2) {
+                        Class<?>[] arg2 = {PRIMS[p0], PRIMS[p1], PRIMS[p2]};
+                        setUpKey(method, arg2);
+                    }
+                }
+            }
+        }
+        keyList = byKey.keySet().toArray(new MethodKey[byKey.size()]);
+    }
+
+    /** Builds a string key */
+    String makeStringKey(String method, Class<?>... params) {
+            StringBuilder builder = new StringBuilder(method);
+            for(int p = 0; p < params.length; ++p) {
+                builder.append(ClassMap.MethodCache.primitiveClass(params[p]).getName());
+            }
+            return builder.toString();
+    }
+    
+    /** Checks that a string key does exist */
+    void checkStringKey(String method, Class<?>... params) {
+        String key = makeStringKey(method, params);
+        MethodKey out = byString.get(key);
+        assertTrue(out != null);
+    }
+        
+    /** Builds a method key */
+    MethodKey makeKey(String method, Class<?>... params) {
+        return new MethodKey(method, params);
+    }
+    
+    /** Checks that a method key exists */
+    void checkKey(String method, Class<?>... params) {
+        MethodKey key = makeKey(method, params);
+        String out = byKey.get(key);
+        assertTrue(out != null);
+    }
+    
+    public void testObjectKey() throws Exception {
+        for(int k = 0; k < keyList.length; ++k) {
+            MethodKey ctl = keyList[k];
+            MethodKey key = makeKey(ctl.getMethod(), ctl.getParameters());
+            String out = byKey.get(key);
+            assertTrue(out != null);
+            assertTrue(ctl.toString() + " != " + out, ctl.toString().equals(out));
+        }
+        
+    }
+    
+    public void testStringKey() throws Exception {
+        for(int k = 0; k < keyList.length; ++k) {
+            MethodKey ctl = keyList[k];
+            String key = makeStringKey(ctl.getMethod(), ctl.getParameters());
+            MethodKey out = byString.get(key);
+            assertTrue(out != null);
+            assertTrue(ctl.toString() + " != " + key, ctl.equals(out));
+        }
+        
+    }
+    
+    private static final int LOOP = 3;//00;
+    
+    public void testPerfKey() throws Exception {
+        for(int l = 0; l < LOOP; ++l)
+        for(int k = 0; k < keyList.length; ++k) {
+            MethodKey ctl = keyList[k];
+            MethodKey key = makeKey(ctl.getMethod(), ctl.getParameters());
+            String out = byKey.get(key);
+            assertTrue(out != null);
+        }
+    }
+    
+    public void testPerfString() throws Exception {
+        for(int l = 0; l < LOOP; ++l)
+        for(int k = 0; k < keyList.length; ++k) {
+            MethodKey ctl = keyList[k];
+            String key = makeStringKey(ctl.getMethod(), ctl.getParameters());
+            MethodKey out = byString.get(key);
+            assertTrue(out != null);
+        }
+    }
+    
+    public void testPerfKey2() throws Exception {
+        for(int l = 0; l < LOOP; ++l)
+        for (int m = 0; m < METHODS.length; ++m) {
+            String method = METHODS[m];
+            for (int p0 = 0; p0 < ARGS.length; ++p0) {
+                checkKey(method, ARGS[p0].getClass());
+                for (int p1 = 0; p1 < ARGS.length; ++p1) {
+                    checkKey(method, ARGS[p0].getClass(), ARGS[p1].getClass());
+                    for (int p2 = 0; p2 < ARGS.length; ++p2) {
+                        checkKey(method, ARGS[p0].getClass(), ARGS[p1].getClass(), ARGS[p2].getClass());
+                    }
+                }
+            }
+        }
+    }
+    
+    public void testPerfStringKey2() throws Exception {
+        for(int l = 0; l < LOOP; ++l)
+        for (int m = 0; m < METHODS.length; ++m) {
+            String method = METHODS[m];
+            for (int p0 = 0; p0 < ARGS.length; ++p0) {
+                checkStringKey(method, ARGS[p0].getClass());
+                for (int p1 = 0; p1 < ARGS.length; ++p1) {
+                    checkStringKey(method,  ARGS[p0].getClass(), ARGS[p1].getClass());
+                    for (int p2 = 0; p2 < ARGS.length; ++p2) {
+                        checkStringKey(method, ARGS[p0].getClass(), ARGS[p1].getClass(), ARGS[p2].getClass());
+                    }
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/junit/Asserter.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/junit/Asserter.java
new file mode 100644
index 0000000..4b086fd
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/junit/Asserter.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.jexl2.junit;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.Assert;
+
+import org.apache.commons.jexl2.Expression;
+import org.apache.commons.jexl2.JexlContext;
+import org.apache.commons.jexl2.MapContext;
+import org.apache.commons.jexl2.JexlEngine;
+
+/**
+ * A utility class for performing JUnit based assertions using Jexl
+ * expressions. This class can make it easier to do unit tests using
+ * Jexl navigation expressions.
+ *
+ * @since 1.0
+ * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
+ * @version $Revision$
+ */
+public class Asserter extends Assert {
+    /** variables used during asserts. */
+    private final Map<String, Object> variables = new HashMap<String, Object>();
+    /** context to use during asserts. */
+    private final JexlContext context = new MapContext(variables);
+
+    /** Jexl engine to use during Asserts. */
+    private final JexlEngine engine;
+
+    /**
+     * 
+     * Create an asserter.
+     */
+    public Asserter(JexlEngine jexl) {
+        engine = jexl;
+    }
+
+
+    /**
+     * Performs an assertion that the value of the given Jexl expression 
+     * evaluates to the given expected value.
+     * 
+     * @param expression is the Jexl expression to evaluate
+     * @param expected is the expected value of the expression
+     * @throws Exception if the expression could not be evaluationed or an assertion
+     * fails
+     */
+    public void assertExpression(String expression, Object expected) throws Exception {
+        Expression exp = engine.createExpression(expression);
+        Object value = exp.evaluate(context);
+
+        assertEquals("expression: " + expression, expected, value);
+    }
+
+    /**
+     * Puts a variable of a certain name in the context so that it can be used from
+     * assertion expressions.
+     * 
+     * @param name variable name
+     * @param value variable value
+     */
+    public void setVariable(String name, Object value) {
+        variables.put(name, value);
+    }
+
+}
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/junit/AsserterTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/junit/AsserterTest.java
new file mode 100644
index 0000000..47a7222
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/junit/AsserterTest.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2.junit;
+
+import junit.framework.AssertionFailedError;
+
+import org.apache.commons.jexl2.JexlEngine;
+import org.apache.commons.jexl2.JexlTestCase;
+import org.apache.commons.jexl2.Foo;
+
+/**
+ *  Simple testcases
+ *
+ *  @since 1.0
+ *  @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
+ *  @version $Id$
+ */
+public class AsserterTest extends JexlTestCase {
+    public AsserterTest(String testName) {
+        super(testName);
+    }
+
+    public void testThis() throws Exception {
+        Asserter asserter = new Asserter(JEXL);
+        asserter.setVariable("this", new Foo());
+        
+        asserter.assertExpression("this.get('abc')", "Repeat : abc");
+        
+        try {
+            asserter.assertExpression("this.count", "Wrong Value");
+            fail("This method should have thrown an assertion exception");
+        }
+        catch (AssertionFailedError e) {
+            // it worked!
+        }
+    }
+
+    public void testVariable() throws Exception {
+        JexlEngine jexl = new JexlEngine();
+        jexl.setSilent(true);
+        Asserter asserter = new Asserter(jexl);
+        asserter.setVariable("foo", new Foo());
+        asserter.setVariable("person", "James");
+
+        asserter.assertExpression("person", "James");
+        asserter.assertExpression("size(person)", new Integer(5));
+        
+        asserter.assertExpression("foo.getCount()", new Integer(5));
+        asserter.assertExpression("foo.count", new Integer(5));
+        
+        try {
+            asserter.assertExpression("bar.count", new Integer(5));
+            fail("This method should have thrown an assertion exception");
+        }
+        catch (AssertionFailedError e) {
+            // it worked!
+        }
+    }
+}
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/junit/package.html b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/junit/package.html
new file mode 100644
index 0000000..7309e67
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/junit/package.html
@@ -0,0 +1,53 @@
+<html>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You 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.
+-->
+ <head>
+  <title>Package Documentation for org.apache.commons.jexl2.junit Package</title>
+ </head>
+ <body bgcolor="white">
+  Using JEXL expressions in JUnit assertions.
+  <br/><br/>
+  <p>
+   <ul>
+    <li><a href="#intro">Introduction</a></li>
+   </ul>
+  </p>
+  <h2><a name="intro">Introduction</a></h2>
+  <p>
+   This package only contains one class, Asserter, which
+   allows you to use a JEXL expression in a JUnit assertion.
+   The following example demonstrates the use of the Asserter
+   class.  An instance is created, and the internal JexlContext
+   is populated via calls to setVariable().  Calls to 
+   assertExpression() succeed if the expression evaluates to
+   the value of the second parameter, otherwise an 
+   AssertionFailedException is thrown.
+  </p>
+  
+  <pre>
+   Asserter asserter = new Asserter();
+   asserter.setVariable("foo", new Foo());
+   asserter.setVariable("person", "James");
+
+   asserter.assertExpression("person", "James");
+   asserter.assertExpression("size(person)", new Integer(5));
+        
+   asserter.assertExpression("foo.getCount()", new Integer(5));
+   asserter.assertExpression("foo.count", new Integer(5));
+  </pre>
+</body>
+</html>
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/parser/ParserTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/parser/ParserTest.java
new file mode 100644
index 0000000..cb8ea15
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/parser/ParserTest.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jexl2.parser;
+
+import java.io.StringReader;
+
+import junit.framework.TestCase;
+
+/**
+ * @since 1.0
+ *
+ */
+public class ParserTest extends TestCase
+{
+    public ParserTest(String testName)
+    {
+        super(testName);
+    }
+
+    /**
+      *  parse test : see if we can parse a little script
+      */
+     public void testParse1()
+         throws Exception
+     {
+         Parser parser = new Parser(new StringReader(";"));
+
+         SimpleNode sn = parser.parse(new StringReader("foo = 1;"), null);
+         assertNotNull("parsed node is null", sn);
+     }
+
+    public void testParse2()
+        throws Exception
+    {
+        Parser parser = new Parser(new StringReader(";"));
+
+        SimpleNode sn = parser.parse(new StringReader("foo = \"bar\";"), null);
+        assertNotNull("parsed node is null", sn);
+
+        sn = parser.parse(new StringReader("foo = 'bar';"), null);
+        assertNotNull("parsed node is null", sn);
+    }
+}
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/scripting/JexlScriptEngineOptionalTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/scripting/JexlScriptEngineOptionalTest.java
new file mode 100644
index 0000000..424d154
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/scripting/JexlScriptEngineOptionalTest.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+
+package org.apache.commons.jexl2.scripting;
+
+import java.io.StringWriter;
+import javax.script.Compilable;
+import javax.script.CompiledScript;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import junit.framework.TestCase;
+
+public class JexlScriptEngineOptionalTest extends TestCase {
+    private final JexlScriptEngineFactory factory = new JexlScriptEngineFactory();
+    private final ScriptEngineManager manager = new ScriptEngineManager();
+    private final ScriptEngine engine = manager.getEngineByName("jexl");
+
+    public void testOutput() throws Exception {
+        String output = factory.getOutputStatement("foo\u00a9bar");
+        assertEquals("JEXL.out.print('foo\\u00a9bar')", output);
+        // redirect output to capture evaluation result
+        final StringWriter outContent = new StringWriter();
+        engine.getContext().setWriter(outContent);
+        engine.eval(output);
+        assertEquals("foo\u00a9bar", outContent.toString());
+    }
+
+    public void testError() throws Exception {
+        String error = "JEXL.err.print('ERROR')";
+        // redirect error to capture evaluation result
+        final StringWriter outContent = new StringWriter();
+        engine.getContext().setErrorWriter(outContent);
+        engine.eval(error);
+        assertEquals("ERROR", outContent.toString());
+    }
+
+    public void testCompilable() throws Exception {
+        assertTrue("Engine should implement Compilable", engine instanceof Compilable);
+        Compilable cengine = (Compilable) engine;
+        CompiledScript script = cengine.compile("40 + 2");
+        assertEquals(Integer.valueOf(42), script.eval());
+        assertEquals(Integer.valueOf(42), script.eval());
+    }
+}
diff --git a/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/scripting/JexlScriptEngineTest.java b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/scripting/JexlScriptEngineTest.java
new file mode 100644
index 0000000..fc7659f
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/java/org/apache/commons/jexl2/scripting/JexlScriptEngineTest.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ */
+
+package org.apache.commons.jexl2.scripting;
+
+import java.io.Reader;
+import java.util.Arrays;
+import java.util.Map;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import junit.framework.TestCase;
+
+public class JexlScriptEngineTest extends TestCase {
+
+    public void testScriptEngineFactory() throws Exception {
+        JexlScriptEngineFactory factory = new JexlScriptEngineFactory();
+        assertEquals("JEXL Engine", factory.getParameter(ScriptEngine.ENGINE));
+        assertEquals("1.0", factory.getParameter(ScriptEngine.ENGINE_VERSION));
+        assertEquals("JEXL", factory.getParameter(ScriptEngine.LANGUAGE));
+        assertEquals("2.0", factory.getParameter(ScriptEngine.LANGUAGE_VERSION));
+        assertEquals(Arrays.asList("JEXL", "Jexl", "jexl"), factory.getParameter(ScriptEngine.NAME));
+        assertNull(factory.getParameter("THREADING"));
+
+        assertEquals(Arrays.asList("jexl"), factory.getExtensions());
+        assertEquals(Arrays.asList("application/x-jexl"), factory.getMimeTypes());
+
+        assertEquals("42;", factory.getProgram(new String[]{"42"}));
+        assertEquals("str.substring(3,4)", factory.getMethodCallSyntax("str", "substring", new String[]{"3", "4"}));
+    }
+
+    public void testScripting() throws Exception {
+        ScriptEngineManager manager = new ScriptEngineManager();
+        assertNotNull("Manager should not be null", manager);
+        ScriptEngine engine = manager.getEngineByName("jexl");
+        assertNotNull("Engine should not be null (name)", engine);
+        engine = manager.getEngineByExtension("jexl");
+        assertNotNull("Engine should not be null (ext)", engine);
+        final Integer initialValue = Integer.valueOf(123);
+        assertEquals(initialValue,engine.eval("123"));
+        assertEquals(initialValue,engine.eval("0;123"));// multiple statements
+        long time1 = System.currentTimeMillis();
+        Long time2 = (Long) engine.eval(
+             "sys=context.class.forName(\"java.lang.System\");"
+            +"now=sys.currentTimeMillis();"
+            );
+        assertTrue("Must take some time to process this",time1 <= time2.longValue());
+        engine.put("value", initialValue);
+        assertEquals(initialValue,engine.get("value"));
+        final Integer newValue = Integer.valueOf(124);
+        assertEquals(newValue,engine.eval("old=value;value=value+1"));
+        assertEquals(initialValue,engine.get("old"));
+        assertEquals(newValue,engine.get("value"));
+        assertEquals(engine.getContext(),engine.get(JexlScriptEngine.CONTEXT_KEY));
+        // Check behaviour of JEXL object
+        assertEquals(engine.getContext().getReader(),engine.eval("JEXL.in"));
+        assertEquals(engine.getContext().getWriter(),engine.eval("JEXL.out"));
+        assertEquals(engine.getContext().getErrorWriter(),engine.eval("JEXL.err"));
+        assertEquals(System.class,engine.eval("JEXL.System"));
+    }
+    
+    public void testNulls() throws Exception {
+        ScriptEngineManager manager = new ScriptEngineManager();
+        assertNotNull("Manager should not be null", manager);
+        ScriptEngine engine = manager.getEngineByName("jexl");
+        assertNotNull("Engine should not be null (name)", engine);
+        try {
+            engine.eval((String)null);
+            fail("Should have caused NPE");
+        } catch (NullPointerException e) {
+            // NOOP
+        }
+        try {
+            engine.eval((Reader)null);
+            fail("Should have caused NPE");
+        } catch (NullPointerException e) {
+            // NOOP
+        }
+    }
+
+    public void testEngineNames() throws Exception {
+        ScriptEngineManager manager = new ScriptEngineManager();
+        assertNotNull("Manager should not be null", manager);
+        ScriptEngine engine = manager.getEngineByName("JEXL");
+        assertNotNull("Engine should not be null (JEXL)", engine);        
+        engine = manager.getEngineByName("Jexl");
+        assertNotNull("Engine should not be null (Jexl)", engine);        
+        engine = manager.getEngineByName("jexl");
+        assertNotNull("Engine should not be null (jexl)", engine);        
+    }
+
+    public void testScopes() throws Exception {
+        ScriptEngineManager manager = new ScriptEngineManager();
+        assertNotNull("Manager should not be null", manager);
+        ScriptEngine engine = manager.getEngineByName("JEXL");
+        assertNotNull("Engine should not be null (JEXL)", engine);
+        manager.put("global",Integer.valueOf(1));
+        engine.put("local", Integer.valueOf(10));
+        manager.put("both",Integer.valueOf(7));
+        engine.put("both", Integer.valueOf(7));
+        engine.eval("local=local+1");
+        engine.eval("global=global+1");
+        engine.eval("both=both+1"); // should update engine value only
+        engine.eval("newvar=42;");
+        assertEquals(Integer.valueOf(2),manager.get("global"));
+        assertEquals(Integer.valueOf(11),engine.get("local"));
+        assertEquals(Integer.valueOf(7),manager.get("both"));
+        assertEquals(Integer.valueOf(8),engine.get("both"));
+        assertEquals(Integer.valueOf(42),engine.get("newvar"));
+        assertNull(manager.get("newvar"));
+    }
+
+    public void testDottedNames() throws Exception {
+        ScriptEngineManager manager = new ScriptEngineManager();
+        assertNotNull("Manager should not be null", manager);
+        ScriptEngine engine = manager.getEngineByName("JEXL");
+        assertNotNull("Engine should not be null (JEXL)", engine);
+        engine.eval("this.is.a.test=null");
+        assertNull(engine.get("this.is.a.test"));
+        assertEquals(Boolean.TRUE, engine.eval("empty(this.is.a.test)"));
+        final Object mymap = engine.eval("testmap={ 'key1' : 'value1', 'key2' : 'value2' }");
+        assertTrue(mymap instanceof Map<?, ?>);
+        assertEquals(2,((Map<?, ?>)mymap).size());
+    }
+}
diff --git a/COMMONS_JEXL_2_0/src/test/scripts/test1.jexl b/COMMONS_JEXL_2_0/src/test/scripts/test1.jexl
new file mode 100644
index 0000000..c21f6bb
--- /dev/null
+++ b/COMMONS_JEXL_2_0/src/test/scripts/test1.jexl
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+ 
+// This tests for JEXL-47. AL header above tests for block comments. 
+
+##
+## This is a test script
+##
+if (out != null) out.println('Starting test script');
+x = 1;
+y = 2;
+result = x * y + 5;
+if (out != null) out.println("The result is " + result);
+## return the result.
+result; // JEXL-44 should ignore "quotes" here
+
+/*
+   Trailing comments are also ignored
+*/
\ No newline at end of file
diff --git a/COMMONS_JEXL_2_0/xdocs/building.xml b/COMMONS_JEXL_2_0/xdocs/building.xml
new file mode 100644
index 0000000..591757a
--- /dev/null
+++ b/COMMONS_JEXL_2_0/xdocs/building.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You 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.
+-->
+<document>
+ <properties>
+  <title>Building Commons JEXL</title>
+  <author email="dev@commons.apache.org">Commons Documentation Team</author>
+ </properties>
+<body>
+<!-- ================================================== -->
+<section name="Overview">
+<p>
+  Commons JEXL uses <a href="http://maven.apache.org">Maven 2.2</a>. The source is
+  <a href="http://svn.apache.org/repos/asf/commons/proper/jexl/trunk/">here</a>.
+</p>
+</section>
+<!-- ================================================== -->
+<section name="Maven Goals">
+The following goals are available.
+<ul>
+<li>mvn clean - clean up</li>
+<li>mvn test - compile and run the unit tests</li>
+<li>mvn site - create the documentation</li>
+<li>mvn package - build the jar</li>
+<li>mvn install - build the jar and install in local maven repository</li>
+</ul>
+</section>
+</body>
+</document>
diff --git a/COMMONS_JEXL_2_0/xdocs/changes.xml b/COMMONS_JEXL_2_0/xdocs/changes.xml
new file mode 100644
index 0000000..223454f
--- /dev/null
+++ b/COMMONS_JEXL_2_0/xdocs/changes.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- 
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+ -->
+
+<document>
+  <properties>
+    <title>Changes</title>
+    <author email="dev@commons.apache.org">Commons Developers</author>
+  </properties>
+  <body>
+    <release version="2.0" date="unreleased">
+        <action dev="henrib" type="add" issue="JEXL-27" due-to="Weikuo Liaw">Bean-ish &amp; ant-ish like assignment</action>
+        <action dev="henrib" type="add" issue="JEXL-19" due-to="Jesse Glick">Ternary operator support</action>
+        <action dev="henrib" type="add" issue="JEXL-46" due-to="Alfred Reibenschuh">adding Perl-like regular-expression operators</action>
+        <action dev="henrib" type="add" issue="JEXL-41" due-to="Alejandro Torras">Support for ${...} and #{...} expressions</action>
+        <action dev="henrib" type="add" issue="JEXL-15" due-to="Paul Libbrecht">User definable functions</action>
+        <action dev="sebb" type="add" issue="JEXL-63">JSR-223 support</action>
+        <action dev="henrib" type="update" issue="JEXL-10" due-to="Paul Libbrecht">Make possible checking for unresolved variables</action>
+        <action dev="henrib" type="update" issue="JEXL-11" due-to="Paul Libbrecht">Don&quot;t make null convertible into anything</action>
+        <action dev="henrib" type="fix" issue="JEXL-47" due-to="sebb">Allow single-line comments with //</action>
+        <action dev="henrib" type="fix" issue="JEXL-44" due-to="sebb">Comments don&quot;t allow double-quotes</action>
+        <action dev="henrib" type="add" issue="JEXL-71" due-to="sebb">Array literal syntax is not supported</action>
+        <action dev="dion" type="fix" issue="JEXL-17" due-to="Nestor Urquiza">allowing quote escaping</action>
+        <action dev="dion" type="fix" issue="JEXL-25" due-to="Marek Lewczuk">Call method with varargs</action>
+        <action dev="dion" type="fix" issue="JEXL-32" due-to="Kedar Dave">BigDecimal values are treated as Long values which results in loss of precision</action>
+        <action dev="dion" type="fix" issue="JEXL-33">Remove unnecessary throws Exception from various classes</action>
+        <action dev="henrib" type="fix" issue="JEXL-50" due-to="sebb">Div operator does not do integer division</action>
+        <action dev="henrib" type="fix" issue="JEXL-87" due-to="sebb">Inconsistent behaviour of arithmetical operations</action>
+        <action dev="henrib" type="fix" issue="JEXL-21" due-to="AC">operator overloading / hooks on operator processing</action>
+        <action dev="henrib" type="add">&quot;new&quot; operator support</action>
+        <action dev="henrib" type="add">Support Unicode escapes in string literals</action>
+        <action dev="henrib" type="update">Various performance enhancements &amp; caches</action>
+    </release>
+    <release version="1.1.1-SNAPSHOT" date="unreleased">
+      <action dev="dion" type="update" issue="JEXL-23">Fix jdk1.3 only code that has crept into Jexl tests</action>
+      <action dev="dion" type="update" issue="JEXL-22" due-to="Randy H.">Allow unicode literals to be used</action>
+    </release>
+    <release version="1.1" date="2006-09-10">
+      <action dev="rahul" type="fix" issue="JEXL-17" due-to="Kohsuke Kawaguchi">Consistently throw ParseException in case of a parsing failure, not an Error.</action>
+      <action dev="dion" type="fix" issue="JEXL-3" due-to="Guido Anzuoni">Allow for static methods to be called on classes and not just objects.</action>
+      <action dev="dion" type="add">Added Script and ScriptFactory to allow scripts to be executed from text, files or a URL.</action>
+      <action dev="dion" type="add">Added implementation for bitwise operators: and, complement, or, xor.</action>
+      <action dev="dion" type="add">Added implementation for the foreach statement.</action>
+      <action dev="dion" type="add">Added implementation for the while statement.</action>
+      <action dev="dion" type="add">Added implementation for block statements, e.g. curly braces containing multiple statements.</action>
+      <action dev="dion" type="add">Added implementation for the if statement.</action>
+      <action dev="dion" type="fix" issue="JEXL-6">Unary minus was only working for integer values.</action>
+      <action dev="dion" type="update">Add @since tags to code so we can track API additions via javadoc</action>
+      <action dev="dion" type="add" issue="JEXL-4" due-to="Barry Lagerweij">Support assignment to variables</action>
+      <action dev="dion" type="fix" issue="JEXL-5">'abc'.substring(0,1+1) is empty (method matching problem)</action>
+    </release>
+    <release version="1.0" date="2004-09-07">
+      <action dev="dion" type="fix">Support ant-style properties</action>
+    </release>
+    <release version="1.0-RC1" date="2004-08-26">
+      <action dev="dion" type="fix" due-to="Geoff Waggott">Fix string concatenation broken for variables</action>
+      <action dev="dion" type="fix" issue="JEXL-12">Implement short circuit logic for boolean and/or</action>
+      <action dev="dion" type="add">Handle any size() method that returns an int</action>
+      <action dev="dion" type="fix" issue="JEXL-9">Can't issue .size() on java.util.Set</action>
+    </release>
+  </body>
+</document>
diff --git a/COMMONS_JEXL_2_0/xdocs/cvs-usage.xml b/COMMONS_JEXL_2_0/xdocs/cvs-usage.xml
new file mode 100644
index 0000000..b29ed50
--- /dev/null
+++ b/COMMONS_JEXL_2_0/xdocs/cvs-usage.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You 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.
+-->
+<document>
+ <properties>
+  <title>Source repository</title>
+  <author email="dev@commons.apache.org">Commons Documentation Team</author>
+ </properties>
+ <body>
+<!-- ================================================== -->
+<section name="Commons JEXL Source repository">
+<p>
+  Apache Commons JEXL is hosted on the Apache
+  <a href="http://subversion.tigris.org/">subversion</a> repository.
+</p>
+<p>
+  The project URL is:<br />
+  <code>http://svn.apache.org/repos/asf/commons/proper/jexl/trunk</code>
+</p>
+<p>
+  The best way to view the repository is via the
+  <a href="http://svn.apache.org/viewvc/commons/proper/jexl/trunk/">subversion viewer</a>.
+</p>
+<p>
+  The alternative is to use the
+  <a href="http://svn.apache.org/repos/asf/commons/proper/jexl/trunk/">native subversion</a> display.
+</p>
+</section>
+<!-- ================================================== -->
+</body>
+</document>
diff --git a/COMMONS_JEXL_2_0/xdocs/download_jexl.xml b/COMMONS_JEXL_2_0/xdocs/download_jexl.xml
new file mode 100644
index 0000000..c55a033
--- /dev/null
+++ b/COMMONS_JEXL_2_0/xdocs/download_jexl.xml
@@ -0,0 +1,145 @@
+<?xml version="1.0"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You 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.
+-->
+<!--
+ +======================================================================+
+ |****                                                              ****|
+ |****      THIS FILE IS GENERATED BY THE COMMONS BUILD PLUGIN      ****|
+ |****                    DO NOT EDIT DIRECTLY                      ****|
+ |****                                                              ****|
+ +======================================================================+
+ | TEMPLATE FILE: download-page-template.xml                            |
+ | commons-build-plugin/trunk/src/main/resources/commons-xdoc-templates |
+ +======================================================================+
+ |                                                                      |
+ | 1) Re-generate using: mvn commons:download-page                      |
+ |                                                                      |
+ | 2) Set the following properties in the component's pom:              |
+ |    - commons.componentid (required, alphabetic, lower case)          |
+ |    - commons.release.version (required)                              |
+ |    - commons.binary.suffix (optional)                                |
+ |      (defaults to "-bin", set to "" for pre-maven2 releases)         |
+ |                                                                      |
+ | 3) Example Properties                                                |
+ |                                                                      |
+ |  <properties>                                                        |
+ |    <commons.componentid>math</commons.componentid>                   |
+ |    <commons.release.version>1.2</commons.release.version>            |
+ |  </properties>                                                       |
+ |                                                                      |
+ +======================================================================+
+-->
+<document>
+  <properties>
+    <title>Download Commons JEXL</title>
+    <author email="dev@commons.apache.org">Commons Documentation Team</author>
+  </properties>
+  <body>
+    <section name="Download Commons JEXL">
+      <p>
+        We recommend you use a mirror to download our release
+        builds, but you <strong>must</strong> verify the integrity of
+        the downloaded files using signatures downloaded from our main 
+        distribution directories. Recent releases (48 hours) may not yet
+        be available from the mirrors.
+      </p>
+
+      <p>
+        You are currently using <b>[preferred]</b>.  If you
+        encounter a problem with this mirror, please select another
+        mirror.  If all mirrors are failing, there are <i>backup</i>
+        mirrors (at the end of the mirrors list) that should be
+        available.
+        <br></br>
+        [if-any logo]<a href="[link]"><img align="right" src="[logo]" border="0"></img></a>[end]
+      </p>
+
+      <form action="[location]" method="get" id="SelectMirror">
+        <p>
+          Other mirrors: 
+          <select name="Preferred">
+          [if-any http]
+            [for http]<option value="[http]">[http]</option>[end]
+          [end]
+          [if-any ftp]
+            [for ftp]<option value="[ftp]">[ftp]</option>[end]
+          [end]
+          [if-any backup]
+            [for backup]<option value="[backup]">[backup] (backup)</option>[end]
+          [end]
+          </select>
+          <input type="submit" value="Change"></input>
+        </p>
+      </form>
+
+      <p>
+        The <code>KEYS</code> link links to the code signing keys used to sign the product.
+        The <code>PGP</code> link downloads the OpenPGP compatible signature from our main site. 
+        The <code>MD5</code> link downloads the checksum from the main site.
+      </p>
+
+      <p>
+        For more information concerning Commons JEXL, see the 
+        <a href="index.html" class="name">Commons JEXL</a> web site.
+      </p>
+
+      <p>
+        <div class="links"><span class="link"><a href="http://www.apache.org/dist/commons/KEYS">KEYS</a></span></div>
+        <ul class="downloads">
+          <li class="group"><div class="links"><span class="label">Binary</span></div>
+            <ul>
+              <li class="download"><a href="[preferred]/commons/jexl/binaries/commons-jexl-2.0.tar.gz">2.0.tar.gz</a>
+                <ul class="attributes">
+                  <li><span class="md5">[<a href="http://www.apache.org/dist/commons/jexl/binaries/commons-jexl-2.0.tar.gz.md5">md5</a>]</span>
+                      <span class="pgp">[<a href="http://www.apache.org/dist/commons/jexl/binaries/commons-jexl-2.0.tar.gz.asc">pgp</a>]</span>
+                  </li>
+                </ul>
+              </li>
+              <li class="download"><a href="[preferred]/commons/jexl/binaries/commons-jexl-2.0.zip">2.0.zip</a>
+                <ul class="attributes">
+                  <li><span class="md5">[<a href="http://www.apache.org/dist/commons/jexl/binaries/commons-jexl-2.0.zip.md5">md5</a>]</span>
+                      <span class="pgp">[<a href="http://www.apache.org/dist/commons/jexl/binaries/commons-jexl-2.0.zip.asc">pgp</a>]</span>
+                  </li>
+                </ul>
+              </li>
+            </ul>
+          </li>
+          <li class="group"><div class="links"><span class="label">Source</span></div>
+            <ul>
+              <li class="download"><a href="[preferred]/commons/jexl/source/commons-jexl-2.0-src.tar.gz">2.0.tar.gz</a>
+                <ul class="attributes">
+                  <li><span class="md5">[<a href="http://www.apache.org/dist/commons/jexl/source/commons-jexl-2.0-src.tar.gz.md5">md5</a>]</span>
+                      <span class="pgp">[<a href="http://www.apache.org/dist/commons/jexl/source/commons-jexl-2.0-src.tar.gz.asc">pgp</a>]</span>
+                  </li>
+                </ul>
+              </li>
+              <li class="download"><a href="[preferred]/commons/jexl/source/commons-jexl-2.0-src.zip">2.0.zip</a>
+                <ul class="attributes">
+                  <li><span class="md5">[<a href="http://www.apache.org/dist/commons/jexl/source/commons-jexl-2.0-src.zip.md5">md5</a>]</span>
+                      <span class="pgp">[<a href="http://www.apache.org/dist/commons/jexl/source/commons-jexl-2.0-src.zip.asc">pgp</a>]</span>
+                  </li>
+                </ul>
+              </li>
+            </ul>
+          </li>
+          <li class="download"><a href="[preferred]/commons/jexl/">browse download area</a></li>
+          <li><a href="http://archive.apache.org/dist/commons/jexl/">archives...</a></li>
+        </ul>
+      </p>
+    </section>
+  </body>
+</document>
diff --git a/COMMONS_JEXL_2_0/xdocs/images/jexl-logo-white.png b/COMMONS_JEXL_2_0/xdocs/images/jexl-logo-white.png
new file mode 100644
index 0000000..3ad2943
--- /dev/null
+++ b/COMMONS_JEXL_2_0/xdocs/images/jexl-logo-white.png
Binary files differ
diff --git a/COMMONS_JEXL_2_0/xdocs/images/jexl-logo-white.xcf b/COMMONS_JEXL_2_0/xdocs/images/jexl-logo-white.xcf
new file mode 100644
index 0000000..3edf5c2
--- /dev/null
+++ b/COMMONS_JEXL_2_0/xdocs/images/jexl-logo-white.xcf
Binary files differ
diff --git a/COMMONS_JEXL_2_0/xdocs/index.xml b/COMMONS_JEXL_2_0/xdocs/index.xml
new file mode 100644
index 0000000..8916bc8
--- /dev/null
+++ b/COMMONS_JEXL_2_0/xdocs/index.xml
@@ -0,0 +1,202 @@
+<?xml version="1.0"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You 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.
+-->
+
+<document>
+    <properties>
+        <title>Commons JEXL Overview</title>
+    </properties>
+
+    <body>
+        <section name="Java Expression Language (JEXL)">
+            <p>
+            JEXL is a library intended to facilitate the implementation of dynamic and scripting features in
+            applications and frameworks.
+            </p>
+            <p>
+            It is a small footprint "glueing" API
+            - the <a href="apidocs/org/apache/commons/jexl2/package-summary.html#usage">core features</a> fit in
+            3 classes and 10 methods - that can be used in various conditions:
+                <ul>
+                    <li>Module or component configuration:
+                        <ul>
+                            <li>Your application has configuration files (eventually generated by a design module)
+                            consumed by the end-user module that would benefit from variables and expressions.
+                            </li>
+                            <li>When it would be convenient to use IOC but overall complexity doesn't require
+                            (or can't depend upon) a full-blown library (Spring, Guice...).
+                            </li>
+                        </ul>
+                    </li>
+                    <li>Loose-coupling of interfaces and implementations or duck-typing:
+                        <ul>
+                            <li>You have optional classes that your code cant consider as compilation dependencies.</li>
+                            <li>You have to integrate and call "legacy" code or use components that you dont want to
+                            strongly depend upon.</li>
+                        </ul>
+                    </li>
+                    <li>Basic scripting features:
+                        <ul><li>Your application lets (advanced) users evaluate or define some simple expressions
+                        like computation formulas.</li></ul>
+                    </li>
+                    <li>Simple template capabilities:
+                        <ul><li>Your application has basic template requirements and JSPs or
+                        Velocity would be overkill or too inconvenient to deploy.</li></ul>
+                    </li>
+                </ul>
+            </p>
+            <p>
+            Its name stands for Java EXpression Language, a simple expression language inspired by Jakarta
+            Velocity and the Expression Language defined in the JavaServer Pages Standard Tag Library version 1.1 (JSTL)
+            and JavaServer Pages version 2.0 (JSP). JEXL 2.0 adds features inspired by
+            <a href="http://java.sun.com/products/jsp/reference/techart/unifiedEL.html">Unified EL</a>.
+            </p>
+            <p>
+            The API and the expression language exploit Java-beans naming patterns through
+            introspection to expose property getters and setters. It also considers public class fields
+            as properties and allows to invoke any accessible method.
+            </p>
+            <p>
+            JEXL attempts to bring some of the lessons learned by the Velocity
+            community about expression languages in templating to a wider audience.
+                <a href="http://commons.apache.org/jelly">Commons Jelly</a> needed
+            Velocity-ish method access, it just had to have it.
+            </p>
+            <p>
+            It must be noted that JEXL is <strong>not</strong> a compatible implementation of EL as defined
+            in JSTL 1.1 (JSR-052) or JSP 2.0 (JSR-152). For a compatible implementation of
+            these specifications, see the <a href="http://commons.apache.org/el">Commons EL</a> project.
+            </p>
+        </section>
+    
+        <section name="A Brief Example">
+            <p>
+            When evaluating expressions, JEXL merges an
+                <a href="apidocs/org/apache/commons/jexl2/Expression.html">Expression</a>
+            with a
+                <a href="apidocs/org/apache/commons/jexl2/JexlContext.html">JexlContext</a>.
+            An Expression is created using
+                <a href="apidocs/org/apache/commons/jexl2/JexlEngine.html#createExpression(java.lang.String)">JexlEngine#createExpression()</a>,
+            passing a String containing valid JEXL syntax.  A simple JexlContext can be created by instantiating a
+                <a href="apidocs/org/apache/commons/jexl2/MapContext.html">MapContext</a>;
+            a map of variables that will be internally wrapped can be optionally provided through its constructor.
+            The following example, takes a variable named foo, and invokes the bar() method on the property innerFoo:
+            </p>
+
+            <source><![CDATA[
+            // Create or retrieve a JexlEngine
+            JexlEngine jexl = new JexlEngine();
+            // Create an expression object
+            String jexlExp = "foo.innerFoo.bar()";
+            Expression e = jexl.createExpression( jexlExp );
+
+            // Create a context and add data
+            JexlContext jc = new MapContext();
+            jc.set("foo", new Foo() );
+
+            // Now evaluate the expression, getting the result
+            Object o = e.evaluate(jc);
+        ]]>
+            </source>
+        </section>
+
+        <section name="Extensions to JSTL Expression Language">
+            <p>
+    While JEXL is similar to the expression language defined in JSTL, it has improved
+    upon the syntax in a few areas:
+            </p>
+            <ul>
+                <li>Support for invocation of any accessible method (see example above).</li>
+                <li>Support for setting/getting any accessible public field.</li>
+                <li>A general <span class="literal">size()</span> method, which works on:
+                    <ul>
+                        <li><span class="literal">String</span> - returns length</li>
+                        <li><span class="literal">Map</span> - returns number of keys</li>
+                        <li><span class="literal">List</span> - returns number of elements.</li>
+                    </ul>
+                </li>
+                <li>A general <span class="literal">empty()</span> method, which works on Collections and Strings.</li>
+                <li>A general <span class="literal">new()</span> method allowing to instantiate objects.</li>
+                <li>Support for the ternary operator 'a ? b : c' - and its GNU-C / "Elvis" variant 'a ?: c'.</li>
+                <li>Support for the Perl-like regex matching operators '=~' and '!~'</li>
+                <li>Misc : '+' has been overloaded to be use as a String concatenation operator</li>
+            </ul>
+
+        </section>
+
+        <section name="Releases">
+            <p>
+        The current released version is 2.0.
+        See the <a href="releases.html">releases</a> page for information on obtaining releases.
+            </p>
+        </section>
+
+        <section name="Related Resources">
+            <p>
+          JEXL is not a product of the Java Community Process (JCP), but it provides a
+          similar expression syntax.  For more information about JSP 2.0 EL and JSTL 1.1
+          EL:
+            </p>
+            <ul>
+                <li>
+                    <a href="http://java.sun.com/products/jsp/index.jsp">JSP 2.0</a> is covered
+            by Java Specification Requests (JSR) 
+                    <a href="http://www.jcp.org/en/jsr/detail?id=152">JSR-152: JavaServer
+            Pages 2.0 Specification</a>.
+                </li>
+                <li>
+            Apache has an implementation of the expression language for JSP 2.0,
+            called <a href="http://commons.apache.org/el/index.html">EL</a>
+                </li>
+                <li>
+                    <a href="http://java.sun.com/products/jsp/jstl/">JSTL 1.1</a> is covered
+            by <a href="http://jcp.org/en/jsr/detail?id=52">JSR 52: A Standard
+            Tag Library for JavaServer Pages</a>. See the
+                    <a href="http://java.sun.com/products/jsp/jstl/1.1/docs/api/index.html">JSTL API</a>.
+                </li>
+          <li>Apache has a <a href="http://jakarta.apache.org/taglibs/doc/standard-doc/intro.html">JSTL Implementation</a>.</li>
+            </ul>
+            <subsection name="Velocity">
+                <p>
+                    <a href="http://jakarta.apache.org/velocity">Jakarta Velocity</a> implements
+            a similar expression language. 
+                </p>
+                <p>
+            In particular the <a href="http://jakarta.apache.org/velocity/user-guide.html#References">References</a>
+            section of the User Guide has some good information on properties and method which correlate
+            directly to JEXL.
+                </p>
+            </subsection>
+        </section>
+
+        <section name="Anyone Using It Yet?">
+            <ul>
+                <li>
+                    <a href="http://commons.apache.org/configuration">Commons Configuration</a>
+                </li>
+                <li>
+                    <a href="http://commons.apache.org/scxml">Commons SCXML</a>
+                </li>
+                <li>
+                    <a href="http://commons.apache.org/jelly">Jelly</a>
+                </li>
+            </ul>
+        </section>
+
+    </body>
+</document>
+
diff --git a/COMMONS_JEXL_2_0/xdocs/issue-tracking.xml b/COMMONS_JEXL_2_0/xdocs/issue-tracking.xml
new file mode 100644
index 0000000..4684cb6
--- /dev/null
+++ b/COMMONS_JEXL_2_0/xdocs/issue-tracking.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You 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.
+-->
+<!--
+ +======================================================================+
+ |****                                                              ****|
+ |****      THIS FILE IS GENERATED BY THE COMMONS BUILD PLUGIN      ****|
+ |****                    DO NOT EDIT DIRECTLY                      ****|
+ |****                                                              ****|
+ +======================================================================+
+ | TEMPLATE FILE: issue-tracking-template.xml                           |
+ | commons-build-plugin/trunk/src/main/resources/commons-xdoc-templates |
+ +======================================================================+
+ |                                                                      |
+ | 1) Re-generate using: mvn commons:jira-page                          |
+ |                                                                      |
+ | 2) Set the following properties in the component's pom:              |
+ |    - commons.jira.id  (required, alphabetic, upper case)             |
+ |    - commons.jira.pid (required, numeric)                            |
+ |                                                                      |
+ | 3) Example Properties                                                |
+ |                                                                      |
+ |  <properties>                                                        |
+ |    <commons.jira.id>MATH</commons.jira.id>                           |
+ |    <commons.jira.pid>12310485</commons.jira.pid>                     |
+ |  </properties>                                                       |
+ |                                                                      |
+ +======================================================================+
+-->
+<document>
+  <properties>
+    <title>Commons JEXL Issue tracking</title>
+    <author email="dev@commons.apache.org">Commons Documentation Team</author>
+  </properties>
+  <body>
+
+    <section name="Commons JEXL Issue tracking">
+      <p>
+      Commons JEXL uses <a href="http://issues.apache.org/jira/">ASF JIRA</a> for tracking issues.
+      See the <a href="http://issues.apache.org/jira/browse/JEXL">Commons JEXL JIRA project page</a>.
+      </p>
+
+      <p>
+      To use JIRA you may need to <a href="http://issues.apache.org/jira/secure/Signup!default.jspa">create an account</a>
+      (if you have previously created/updated Commons issues using Bugzilla an account will have been automatically
+      created and you can use the <a href="http://issues.apache.org/jira/secure/ForgotPassword!default.jspa">Forgot Password</a>
+      page to get a new password).
+      </p>
+
+      <p>
+      If you would like to report a bug, or raise an enhancement request with
+      Commons JEXL please do the following:
+      <ol>
+        <li><a href="http://issues.apache.org/jira/secure/IssueNavigator.jspa?reset=true&amp;pid=12310479&amp;sorter/field=issuekey&amp;sorter/order=DESC&amp;status=1&amp;status=3&amp;status=4">Search existing open bugs</a>.
+            If you find your issue listed then please add a comment with your details.</li>
+        <li><a href="mail-lists.html">Search the mailing list archive(s)</a>.
+            You may find your issue or idea has already been discussed.</li>
+        <li>Decide if your issue is a bug or an enhancement.</li>
+        <li>Submit either a <a href="http://issues.apache.org/jira/secure/CreateIssueDetails!init.jspa?pid=12310479&amp;issuetype=1&amp;priority=4&amp;assignee=-1">bug report</a>
+            or <a href="http://issues.apache.org/jira/secure/CreateIssueDetails!init.jspa?pid=12310479&amp;issuetype=4&amp;priority=4&amp;assignee=-1">enhancement request</a>.</li>
+      </ol>
+      </p>
+
+      <p>
+      Please also remember these points:
+      <ul>
+        <li>the more information you provide, the better we can help you</li>
+        <li>test cases are vital, particularly for any proposed enhancements</li>
+        <li>the developers of Commons JEXL are all unpaid volunteers</li>
+      </ul>
+      </p>
+
+      <p>
+      For more information on subversion and creating patches see the
+      <a href="http://www.apache.org/dev/contributors.html">Apache Contributors Guide</a>.
+      </p>
+
+      <p>
+      You may also find these links useful:
+      <ul>
+        <li><a href="http://issues.apache.org/jira/secure/IssueNavigator.jspa?reset=true&amp;pid=12310479&amp;sorter/field=issuekey&amp;sorter/order=DESC&amp;status=1&amp;status=3&amp;status=4">All Open Commons JEXL bugs</a></li>
+        <li><a href="http://issues.apache.org/jira/secure/IssueNavigator.jspa?reset=true&amp;pid=12310479&amp;sorter/field=issuekey&amp;sorter/order=DESC&amp;status=5&amp;status=6">All Resolved Commons JEXL bugs</a></li>
+        <li><a href="http://issues.apache.org/jira/secure/IssueNavigator.jspa?reset=true&amp;pid=12310479&amp;sorter/field=issuekey&amp;sorter/order=DESC">All Commons JEXL bugs</a></li>
+      </ul>
+      </p>
+    </section>
+  </body>
+</document>
diff --git a/COMMONS_JEXL_2_0/xdocs/reference/examples.xml b/COMMONS_JEXL_2_0/xdocs/reference/examples.xml
new file mode 100644
index 0000000..3c51b17
--- /dev/null
+++ b/COMMONS_JEXL_2_0/xdocs/reference/examples.xml
@@ -0,0 +1,187 @@
+<?xml version="1.0"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You 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.
+-->
+
+<document>
+  <properties>
+    <title>Commons JEXL Examples</title>
+  </properties>
+
+  <body>
+    <section name="Overview">
+      <p>
+        In this reference you will find the following topics to help with your use of JEXL.
+        <ul>
+          <li><a href="#Evaluating Expressions">Evaluating Expressions</a></li>
+          <li><a href="#Custom Contexts">Custom Contexts</a></li>
+          <li><a href="#Example Expressions">Example Expressions</a></li>
+        </ul>
+      </p>
+      <p>
+        You can find two sample programs in JEXL's CVS repository:
+        <ul>
+          <li><a href="http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/org/apache/commons/jexl/examples/ArrayTest.java?view=markup">Using arrays</a></li>
+          <li><a href="http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/org/apache/commons/jexl/examples/MethodPropertyTest.java?view=markup">Accessing Properties and invoking methods</a></li>
+        </ul>
+      </p>
+      <p>
+        As well, <a href="http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/org/apache/commons/jexl/JexlTest.java?view=markup">JEXL's Unit Tests</a>
+        provide handy examples of expressions. The test code also contains a
+        <a href="http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/org/apache/commons/jexl/Jexl.java?view=markup">simple class</a>
+        that evaluates its command line arguments as JEXL expressions when run.
+      </p>
+    </section>
+    <section name="Evaluating Expressions">
+      <p>
+        To evaluate expressions using JEXL, you need two things:
+        <ul>
+          <li>A <a href="http://commons.apache.org/jexl/apidocs/org/apache/commons/jexl/JexlContext.html">context</a> containing any variables, and</li>
+          <li>An <a href="http://commons.apache.org/jexl/apidocs/org/apache/commons/jexl/Expression.html">expression</a></li>
+        </ul>
+      </p>
+      <p>
+        The easiest way of obtaining a a context is to use the
+        <a href="http://commons.apache.org/jexl/apidocs/org/apache/commons/jexl/JexlHelper.html#createContext()">new JexlContext.Mapped()</a>
+        method. This creates a context which is simply an extension of a
+        <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/HashMap.html">HashMap</a>
+      </p>
+      <p>
+        <a href="http://commons.apache.org/jexl/apidocs/org/apache/commons/jexl/Expression.html">Expressions</a> are
+        created using the <a href="http://commons.apache.org/jexl/apidocs/org/apache/commons/jexl/ExpressionFactory.html#createExpression(java.lang.String)">ExpressionFactory.createExpression(String)</a>
+        method. 
+      </p>
+      <p>
+        Once you have your expression, you can then use use the
+        <a href="http://commons.apache.org/jexl/apidocs/org/apache/commons/jexl/Expression.html#evaluate(org.apache.commons.jexl2.JexlContext)">evaluate</a>
+        to execute it and obtain a result.
+      </p>
+      <p>
+        Here's a typical scenario:
+      </p>
+      <source>
+    // Create an expression object for our calculation
+    String calculateTax = taxManager.getTaxCalc(); //e.g. "((G1 + G2 + G3) * 0.1) + G4";
+    Expression e = ExpressionFactory.createExpression( calculateTax );
+
+    // populate the context
+    JexlContext context = new JexlContext.Mapped();
+    context.set("G1", businessObject.getTotalSales());
+    context.set("G2", taxManager.getTaxCredit(businessObject.getYear()));
+    context.set("G3", businessObject.getIntercompanyPayments());
+    context.set("G4", -taxManager.getAllowances());
+    // ...
+    
+    // work it out
+    Float result = (Float)e.evaluate(context);
+      </source>
+    </section>
+    <section name="Custom Contexts">
+      <p>
+        Often you have the objects and values that are needed in the context available
+        elsewhere, and instead of creating the default context and populating it
+        manually in the code, it may be simpler to create a context implementation of your
+        own.
+      </p>
+      <p>
+        The <a href="http://commons.apache.org/jexl/apidocs/org/apache/commons/jexl/JexlContext.html">JexlContext</a>
+        interface is very simple with only two methods, one to get the variables of the
+        context as a <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/util/Map.html">Map</a> and
+        another to set the variables of the context from a Map.
+      </p>
+      <p>
+        Here's a simple context that wraps the JVM's system properties:
+        <source>
+        JexlContext context = new JexlContext() {
+            public Map getVars() { return System.getProperties(); }
+            public void setVars(Map map) { }
+        };
+        </source>
+      </p>
+    </section>
+    <section name="Example Expressions">
+      <!--  invoking methods, property access, array access, empty, size etc... -->
+      <subsection name="Arithmetic">
+        <p>Most valid arithmetic expressions in Java are also valid in Jexl.</p>
+        <source>
+1 + 2
+12.0 - 5.2
+6 * 12 + 5 / 2.6
+12 % 2
+6 / 4
+-12 + 77.2
+x * 1.1 + y
+        </source>
+        <p>Arithmetic expressions can use variables. <code>null</code> can be treated as a zero for arithmetic.</p>
+      </subsection>
+      <subsection name="Calling methods">
+        <p>
+          JEXL allows you to call any method on a Java object using the same syntax.
+          If you have a string in the context under the name <code>aString</code>,
+          you could call it's <code>length</code> 
+          method like this:
+          <source>
+aString.length()
+aString.substring(6)
+          </source>
+        </p>
+        <p>
+          Often the values you want to pass to a method are other variables or expressions.
+          If you have a number in the context, named <code>i</code>, you could use it
+          in a method call:
+          <source>aString.substring(i)</source>
+        </p>
+      </subsection>
+      <subsection name="Accessing properties">
+        <p>
+          JEXL provides a shorthand syntax to access methods that
+          follow the JavaBean naming convention for properties, i.e.
+          setters and getters.
+        </p>
+        <p>
+          If you have some object foo in the context and it has a
+          method <code>getBar()</code>, you can call that method using
+          the following syntax:
+          <source>foo.bar</source>
+        </p>
+        <p>
+          Since <code>java.lang.Object</code> has a <code>getClass()</code> method
+          that returns a <code>java.lang.Class</code> object, and the
+          class has a <code>getName()</code> method, the following is a shorthand
+          for obtaining the class name of an object <code>foo</code> in the context: 
+          <source>foo.class.name</source>
+        </p>
+      </subsection>
+      <subsection name="Arrays, Lists and Maps">
+        <p>
+          Array elements can be accessed using either square brackets or a dotted
+          index notation, e.g. the following are equivalent
+          <source>arr[0]
+arr.0</source>
+          The same holds true for lists.
+        </p>
+        <p>
+          For a map, the syntax is very similar, except the 'index' is an object, e.g.
+          the following are equivalent.
+          <source>aMap['key']
+aMap.get('key')</source>
+          Please note that the index does not have to be a string, and
+          that the string usage above is just one possible option.
+        </p>
+      </subsection>
+    </section>
+  </body>
+</document>
diff --git a/COMMONS_JEXL_2_0/xdocs/reference/index.xml b/COMMONS_JEXL_2_0/xdocs/reference/index.xml
new file mode 100644
index 0000000..517f82f
--- /dev/null
+++ b/COMMONS_JEXL_2_0/xdocs/reference/index.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You 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.
+-->
+
+<document>
+  <properties>
+    <title>Commons JEXL Reference</title>
+  </properties>
+
+  <body>
+    <section name="Reference">
+      <p>
+        The JEXL Reference documentation is made up of the following:
+        <ul>
+          <li><a href="syntax.html">JEXL Syntax</a></li>
+          <li><a href="examples.html">Common examples</a> using JEXL and the expression language</li>
+          <li><a href="jsr223.html">JSR-223 (scripting)</a> using JEXL via JSR-223 (scripting)</li>
+        </ul>
+      </p>
+    </section>
+  </body>
+</document>
+    
diff --git a/COMMONS_JEXL_2_0/xdocs/reference/jsr223.xml b/COMMONS_JEXL_2_0/xdocs/reference/jsr223.xml
new file mode 100644
index 0000000..cb80cbb
--- /dev/null
+++ b/COMMONS_JEXL_2_0/xdocs/reference/jsr223.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You 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.
+-->
+
+<document>
+  <properties>
+    <title>Commons JEXL JSR-223 (scripting) Reference</title>
+  </properties>
+
+  <body>
+    <section name="Overview">
+      <p>
+      Commons JEXL includes JSR-223 (javax.script) support.
+      The binary jar includes the scripting factory
+      and the services definition javax.script.ScriptEngineFactory, 
+      so no special configuration is needed.
+      </p>
+    </section>
+    <section name="Script engine support">
+      <p>
+      The provided script engine implements the following:
+      <ul>
+      <li>Language names: "JEXL", "Jexl", "jexl"</li>
+      <li>Extensions: ".jexl"</li>
+      <li>Mime-types: "application/x-jexl"</li>
+      </ul>
+      The implementation adds an instance of 
+      <a href="http://commons.apache.org/jexl/apidocs/org/apache/commons/jexl/scripting/JexlScriptObject.html">JexlScriptObject</a>
+      to the engine context as the variable "JEXL". 
+      This gives scripts easier access to various items such as System.out and a logger.
+      </p>
+    </section>
+    <section name="Using the JSR-223 JEXL test application">
+        <p>
+        The binary release includes a command-line application which can be used to exercise the JSR-223 script engine.
+        For example:
+        <source>java -cp commons-jexl-2.0.jar;commons-logging-1.1.1.jar[;bsf-api-3.0.jar] org.apache.commons.jexl2.scripting.Main script.jexl</source>
+        If a single argument is provided, then that is assumed to be the name of a script file;
+        otherwise, the application prompts for script input to be evaluated.
+        In both cases, the variable "args" contains the command-line arguments.
+        [Note that Java 1.5 does not include javax.script support; you will need to use the Apache BSF API jar as indicated.]
+        </p>
+    </section>
+    <section name="Using JEXL with JSR-223 on Java 1.5">
+      <p>
+      In order to use JEXL via JSR-223 on Java 1.5, you need to add Apache BSF-API 3.0 jar to the classpath.
+      JEXL also requires Commons Logging on the classpath.
+      </p>
+    </section>
+    <section name="Using JEXL with JSR-223 on Java 1.6+">
+      <p>
+      JSR-223 support is included with Java 1.6+. 
+      JEXL requires Commons Logging, which needs to be included in the path.
+      </p>
+    </section>
+    <section name="JSR-223 support classes">
+      <p>
+        The classes used to support JSR-223 scripting access are:
+        <ul>
+        <li>org.apache.commons.jexl2.scripting.JexlScriptEngineFactory - the factory</li>
+        <li>org.apache.commons.jexl2.scripting.JexlScriptEngine - the engine</li>
+        <li>org.apache.commons.jexl2.scripting.JexlScriptObject - class used to give scripts access to JEXL objects</li>
+        </ul>
+      </p>
+    </section>
+  </body>
+</document>
+    
diff --git a/COMMONS_JEXL_2_0/xdocs/reference/syntax.xml b/COMMONS_JEXL_2_0/xdocs/reference/syntax.xml
new file mode 100644
index 0000000..8d8339b
--- /dev/null
+++ b/COMMONS_JEXL_2_0/xdocs/reference/syntax.xml
@@ -0,0 +1,505 @@
+<?xml version="1.0"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You 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.
+-->
+
+<document>
+  <properties>
+    <title>Commons JEXL Syntax</title>
+  </properties>
+
+  <body>
+    <section name="Overview">
+      <p>
+        This reference is split up into the following sections:
+        <ol>
+          <li><a href="#Language Elements">Language Elements</a></li>
+          <li><a href="#Literals">Literals</a></li>
+          <li><a href="#Functions">Functions</a></li>
+          <li><a href="#Operators">Operators</a></li>
+          <li><a href="#Conditional">Conditional Statements</a></li>
+        </ol>
+      </p>
+      <p>
+        For more technical information about the JEXL Grammar, you can find the
+        <a href="https://javacc.dev.java.net/">JavaCC</a> grammar for JEXL
+        here: <a href="http://svn.apache.org/viewcvs.cgi/jakarta/commons/proper/jexl/trunk/src/java/org/apache/commons/jexl/parser/Parser.jj?view=markup">Parser.jj</a>
+      </p>
+    </section>
+    <section name="Language Elements">
+      <table>
+        <tr><th width="15%">Item</th><th>Description</th></tr>
+        <tr>
+          <td>Comments</td>
+          <td>
+            Specified using <code>##</code> or <code>//</code>and extend to the end of line, e.g.
+            <source>## This is a comment</source>
+            Also specified using <code>//</code>, e.g.
+            <source>// This is a comment</source>
+            Multiple lines comments are specified using <code>/*...*/</code>, e.g.
+            <source>/* This is a
+            multi-line comment */</source>
+          </td>
+        </tr>
+        <tr>
+          <td>Identifiers / variables</td>
+          <td>
+            Must start with <code>a-z</code>, <code>A-Z</code>, <code>_</code> or <code>$</code>.
+            Can then be followed by <code>0-9</code>, <code>a-z</code>, <code>A-Z</code>, <code>_</code> or <code>$</code>.
+            e.g.
+            <ul>
+              <li>Valid: <code>var1</code>,<code>_a99</code>,<code>$1</code></li>
+              <li>Invalid: <code>9v</code>,<code>!a99</code>,<code>1$</code></li>
+            </ul>
+            <p>
+            Variable names are <strong>case-sensitive</strong>, e.g. <code>var1</code> and <code>Var1</code> are different variables.
+            </p>
+            <p>
+              <strong>NOTE:</strong> JEXL does not support variables with hyphens in them, e.g.
+              <source>commons-logging // invalid variable name (hyphenated)</source> is not a valid variable, but instead is treated as a
+              subtraction of the variable <code>logging</code> from the variable <code>commons</code>
+            </p>
+            <p>
+              JEXL also supports <code>ant-style</code> variables, the following is a valid variable name:
+              <source>my.dotted.var</source>
+            </p>
+            <p>
+              <strong>N.B.</strong> the following keywords are reserved, and cannot be used as a variable name or property when using the dot operator:
+              <code>or and eq ne lt gt le ge div mod not null true false new</code>
+              For example, the following is invalid:
+              <source>my.new.dotted.var // invalid ('new' is keyword)</source>
+              In such cases, the [ ] operator can be used, for example:
+              <source>my['new'].dotted.var</source>
+            </p>
+          </td>
+        </tr>
+        <tr>
+          <td>Scripts</td>
+          <td>
+            A script in Jexl is made up of zero or more statements. Scripts can be read from a String, File or URL.
+          </td>
+        </tr>
+        <tr>
+          <td>Statements</td>
+          <td>
+            A statement can be the empty statement, the semicolon (<code>;</code>) ,  block, assignment or an expression.
+            Statements are optionally terminated with a semicolon.
+          </td>
+        </tr>
+        <tr>
+          <td>Block</td>
+          <td>
+            A block is simply multiple statements inside curly braces (<code>{, }</code>).
+          </td>
+        </tr>
+        <tr>
+          <td>Assignment</td>
+          <td>
+            Assigns the value of a variable  (<code>my.var = 'a value'</code>) using a
+            <code>JexlContext</code> as initial resolver. Both <em>beans</em> and <em>ant-ish</em>
+            variables assignment are supported.
+          </td>
+        </tr>
+        <tr>
+          <td>Method calls</td>
+          <td>
+            Calls a method of an object, e.g.
+            <source>"hello world".hashCode()</source> will call the <code>hashCode</code> method
+            of the <code>"hello world"</code> String.
+            <p>In case of multiple arguments and overloading, Jexl will make the best effort to find
+            the most appropriate non ambiguous method to call.</p>
+          </td>
+        </tr>
+      </table>
+    </section>
+    <section name="Literals">
+      <table>
+        <tr><th width="15%">Item</th><th>Description</th></tr>
+        <tr>
+          <td>Integer Literals</td>
+          <td>1 or more digits from <code>0</code> to <code>9</code></td>
+        </tr>
+        <tr>
+          <td>Floating point Literals</td>
+          <td>
+            1 or more digits from <code>0</code> to <code>9</code>, followed
+            by a decimal point and then one or more digits from
+            <code>0</code> to <code>9</code>.
+          </td>
+        </tr>
+        <tr>
+          <td>String literals</td>
+          <td>
+            Can start and end with either <code>'</code> or <code>"</code> delimiters, e.g.
+            <source>"Hello world"</source> and
+            <source>'Hello world'</source> are equivalent.
+            <p>The escape character is <code>\</code>; it only escapes the string delimiter</p>
+          </td>
+        </tr>
+        <tr>
+          <td>Boolean literals</td>
+          <td>
+            The literals <code>true</code> and <code>false</code> can be used, e.g.
+            <source>val1 == true</source>
+          </td>
+        </tr>
+        <tr>
+          <td>Null literal</td>
+          <td>
+            The null value is represented as in java using the literal <code>null</code>, e.g.
+            <source>val1 == null</source>
+          </td>
+        </tr>
+        <tr>
+          <td>Array literal</td>
+          <td>
+            A <code>[</code> followed by one or more expressions separated by <code>,</code> and ending
+            with <code>]</code>, e.g.
+            <source>[ 1, 2, "three" ]</source>
+            <p>This syntax creates an <code>Object[]</code>.</p>
+            <p>
+            JEXL will attempt to strongly type the array; if all entries are of the same class or if all
+            entries are Number instance, the array literal will be an <code>MyClass[]</code> in the former
+            case, a <code>Number[]</code> in the latter case.</p>
+            <p>Furthermore, if all entries in the array literal are of the same class
+            and that class has an equivalent primitive type, the array returned will be a primitive array. e.g.
+            <code>[1, 2, 3]</code> will be interpreted as <code>int[]</code>.</p>
+          </td>
+        </tr>
+        <tr>
+          <td>Map literal</td>
+          <td>
+            A <code>{</code> followed by one or more sets of <code>key : value</code> pairs separated by <code>,</code> and ending
+            with <code>}</code>, e.g.
+            <source>{ "one" : 1, "two" : 2, "three" : 3, "more": "many more" }</source>
+            <p>This syntax creates a <code>HashMap&lt;Object,Object&gt;</code>.</p>
+          </td>
+        </tr>
+      </table>
+    </section>
+    <section name="Functions">
+      <table>
+        <tr><th width="15%">Function</th><th>Description</th></tr>
+        <tr>
+          <td>empty</td>
+          <td>
+            Returns true if the expression following is either:
+            <ol>
+              <li><code>null</code></li>
+              <li>An empty string</li>
+              <li>An array of length zero</li>
+              <li>A collection of size zero</li>
+              <li>An empty map</li>
+            </ol>
+            <source>empty(var1)</source>
+          </td>
+        </tr>
+        <tr>
+          <td>size</td>
+          <td>
+            Returns the information about the expression:
+            <ol>
+              <li>Length of an array</li>
+              <li>Size of a List</li>
+              <li>Size of a Map</li>
+              <li>Size of a Set</li>
+              <li>Length of a string</li>
+            </ol>
+            <source>size("Hello")</source> returns 5.
+          </td>
+        </tr>
+        <tr>
+          <td>new</td>
+          <td>
+            Creates a new instance using a fully-qualified class name or Class:
+            <source>new("java.lang.Double", 10)</source> returns 10.0.
+            <p>Note that the first argument of <code>new</code> can be a variable or any
+            expression evaluating as a String or Class; the rest of the arguments are used
+            as arguments to the constructor for the class considered.</p>
+            <p>In case of multiple constructors, Jexl will make the best effort to find
+            the most appropriate non ambiguous constructor to call.</p>
+          </td>
+        </tr>
+        <tr>
+          <td>ns:function</td>
+          <td>
+            A <code>JexlEngine</code> can register objects or classes used as function namespaces.
+            This can allow expressions like:
+            <source>math:cosinus(23.0)</source>
+          </td>
+        </tr>
+      </table>
+    </section>
+    <section name="Operators">
+      <table>
+        <tr><th width="15%">Operator</th><th>Description</th></tr>
+        <tr>
+          <td>Boolean <code>and</code></td>
+          <td>
+            The usual <code>&amp;&amp;</code> operator can be used as well as the word <code>and</code>, e.g.
+            <source>cond1 and cond2</source> and
+            <source>cond1 &amp;&amp; cond2</source> are equivalent
+          </td>
+        </tr>
+        <tr>
+          <td>Boolean <code>or</code></td>
+          <td>
+            The usual <code>||</code> operator can be used as well as the word <code>or</code>, e.g.
+            <source>cond1 or cond2</source> and
+            <source>cond1 || cond2</source> are equivalent
+          </td>
+        </tr>
+        <tr>
+          <td>Boolean <code>not</code></td>
+          <td>
+            The usual <code>!</code> operator can be used as well as the word <code>not</code>, e.g.
+            <source>!cond1</source> and
+            <source>not cond1</source> are equivalent
+          </td>
+        </tr>
+        <tr>
+          <td>Bitwise <code>and</code></td>
+          <td>
+            The usual <code>&amp;</code> operator is used, e.g.
+            <source>33 &amp; 4</source>, 0010 0001 &amp; 0000 0100 = 0.
+          </td>
+        </tr>
+        <tr>
+          <td>Bitwise <code>or</code></td>
+          <td>
+            The usual <code>|</code> operator is used, e.g.
+            <source>33 | 4</source>, 0010 0001 | 0000 0100 = 0010 0101 = 37.
+          </td>
+        </tr>
+        <tr>
+          <td>Bitwise <code>xor</code></td>
+          <td>
+            The usual <code>^</code> operator is used, e.g.
+            <source>33 ^ 4</source>, 0010 0001 ^ 0000 0100 = 0010 0100 = 37.
+          </td>
+        </tr>
+        <tr>
+          <td>Bitwise <code>complement</code></td>
+          <td>
+            The usual <code>~</code> operator is used, e.g.
+            <source>~33</source>, ~0010 0001 = 1101 1110 = -34.
+          </td>
+        </tr>
+        <tr>
+          <td>Ternary conditional <code>?:</code> </td>
+          <td>
+            The usual ternary conditional operator <code>condition ? if_true : if_false</code> operator can be
+            used as well as the abbreviation <code>value ?: if_false</code> which returns the <code>value</code> if
+            its evaluation is defined, non-null and non-false, e.g.
+            <source>val1 ? val1 : val2</source> and
+            <source>val1 ?: val2 </source> are equivalent.
+            <p>
+              <strong>NOTE:</strong> The condition will evaluate to <code>false</code> when it
+              refers to an undefined variable or <code>null</code> for all <code>JexlEngine</code>
+              flag combinations. This allows explicit syntactic leniency and treats the condition
+              'if undefined or null or false' the same way in all cases.
+            </p>
+          </td>
+        </tr>
+        <tr>
+          <td>Equality</td>
+          <td>
+            The usual <code>==</code> operator can be used as well as the abbreviation <code>eq</code>.
+            For example
+            <source>val1 == val2</source> and
+            <source>val1 eq val2</source> are equivalent.
+            <ol>
+              <li>
+                <code>null</code> is only ever equal to null, that is if you compare null
+                to any non-null value, the result is false.
+              </li>
+              <li>Equality uses the java <code>equals</code> method</li>
+            </ol>
+          </td>
+        </tr>
+        <tr>
+          <td>Inequality</td>
+          <td>
+            The usual <code>!=</code> operator can be used as well as the abbreviation <code>ne</code>.
+            For example
+            <source>val1 != val2</source> and
+            <source>val1 ne val2</source> are equivalent.
+          </td>
+        </tr>
+        <tr>
+          <td>Less Than</td>
+          <td>
+            The usual <code>&lt;</code> operator can be used as well as the abbreviation <code>lt</code>.
+            For example
+            <source>val1 &lt; val2</source> and
+            <source>val1 lt val2</source> are equivalent.
+          </td>
+        </tr>
+        <tr>
+          <td>Less Than Or Equal To</td>
+          <td>
+            The usual <code>&lt;=</code> operator can be used as well as the abbreviation <code>le</code>.
+            For example
+            <source>val1 &lt;= val2</source> and
+            <source>val1 le val2</source> are equivalent.
+          </td>
+        </tr>
+        <tr>
+          <td>Greater Than</td>
+          <td>
+            The usual <code>&gt;</code> operator can be used as well as the abbreviation <code>gt</code>.
+            For example
+            <source>val1 &gt; val2</source> and
+            <source>val1 gt val2</source> are equivalent.
+          </td>
+        </tr>
+        <tr>
+          <td>Greater Than Or Equal To</td>
+          <td>
+            The usual <code>&gt;=</code> operator can be used as well as the abbreviation <code>ge</code>.
+            For example
+            <source>val1 &gt;= val2</source> and
+            <source>val1 ge val2</source> are equivalent.
+          </td>
+        </tr>
+        <tr>
+          <td>Regex match <code>=~</code></td>
+          <td>
+            The Perl inspired <code>=~</code> operator can be used to check that a <code>string</code> matches
+            a regular expression (expressed either a Java String or a java.util.regex.Pattern).
+            For example
+            <code>"abcdef" =~ "abc.*</code> returns <code>true</code>.
+          </td>
+        </tr>
+        <tr>
+          <td>Regex no-match <code>!~</code></td>
+          <td>
+            The Perl inspired <code>!~</code> operator can be used to check that a <code>string</code> does not
+            match a regular expression (expressed either a Java String or a java.util.regex.Pattern).
+            For example
+            <code>"abcdef" !~ "abc.*</code> returns <code>false</code>.
+          </td>
+        </tr>
+        <tr>
+          <td>Addition</td>
+          <td>
+            The usual <code>+</code> operator is used.
+            For example
+            <source>val1 + val2</source>
+          </td>
+        </tr>
+        <tr>
+          <td>Subtraction</td>
+          <td>
+            The usual <code>-</code> operator is used.
+            For example
+            <source>val1 - val2</source>
+          </td>
+        </tr>
+        <tr>
+          <td>Multiplication</td>
+          <td>
+            The usual <code>*</code> operator is used.
+            For example
+            <source>val1 * val2</source>
+          </td>
+        </tr>
+        <tr>
+          <td>Division</td>
+          <td>
+            The usual <code>/</code> operator is used, or one can use the <code>div</code> operator.
+            For example
+            <source>val1 / val2</source>
+            or
+            <source>val1 div val2</source>
+          </td>
+        </tr>
+        <tr>
+          <td>Modulus (or remainder)</td>
+          <td>
+            The <code>%</code> operator is used. An alternative is the <code>mod</code>
+            operator.
+            For example
+            <source>5 mod 2</source> gives 1 and is equivalent to <source>5 % 2</source>
+          </td>
+        </tr>
+        <tr>
+          <td>Negation</td>
+          <td>
+            The unary <code>-</code> operator is used.
+            For example
+            <source>-12</source>
+          </td>
+        </tr>
+        <tr>
+          <td>Array access</td>
+          <td>
+            Array elements may be accessed using either square brackets or a dotted numeral, e.g.
+            <source>arr1[0]</source> and <source>arr1.0</source> are equivalent
+          </td>
+        </tr>
+        <tr>
+          <td>HashMap access</td>
+          <td>
+            Map elements are accessed using square brackets, e.g.
+            <source>map[0]; map['name']; map[var];</source>
+            Note that <source>map['7']</source> and <source>map[7]</source> refer to different elements.
+            Map elements with a numeric key may also be accessed using a dotted numeral, e.g.
+            <source>map[0]</source> and <source>map.0</source> are equivalent.
+          </td>
+        </tr>
+      </table>
+    </section>
+    <section name="Conditional">
+      <table>
+        <tr><th width="15%">Operator</th><th>Description</th></tr>
+        <tr>
+          <td>if</td>
+          <td>
+            Classic, if/else statement, e.g.
+            <source>if ((x * 2) == 5) {
+    y = 1;
+} else {
+    y = 2;
+}</source>
+          </td>
+        </tr>
+        <tr>
+          <td>for</td>
+          <td>
+            Loop through items of an Array, Collection, Map, Iterator or Enumeration, e.g.
+            <source>for(item : list) {
+    x = x + item; 
+}</source>
+            Where <code>item</code> and <code>list</code> are variables.
+            The JEXL 1.1 syntax using <code>foreach(item in list)</code> is now <strong>deprecated</strong>.
+          </td>
+        </tr>
+        <tr>
+          <td>while</td>
+          <td>
+            Loop until a condition is satisfied, e.g.
+            <source>while (x lt 10) {
+    x = x + 2; 
+}</source>
+          </td>
+        </tr>
+      </table>
+    </section>
+
+  </body>
+</document>
+
diff --git a/COMMONS_JEXL_2_0/xdocs/releases.xml b/COMMONS_JEXL_2_0/xdocs/releases.xml
new file mode 100644
index 0000000..39216a4
--- /dev/null
+++ b/COMMONS_JEXL_2_0/xdocs/releases.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<document>
+    <properties>
+        <title>Downloads</title>
+    </properties>
+
+    <body>
+        <section name="Releases">
+            <p><strong>Latest Stable Release</strong></p>
+            <ul>
+                <li>
+                    <a href="http://commons.apache.org/downloads/download_jexl.cgi">2.0 Binary/Source</a>
+                </li>
+            </ul>
+            <p><strong>Nightly Builds</strong></p>
+            <ul>
+                <li>
+                    <a href="http://people.apache.org/builds/commons/nightly/commons-jexl/">Binary/Source</a>
+                </li>
+            </ul>
+            <p>
+                <strong>Archived Releases</strong>
+                <br/>
+             Older releases are retained by the Apache Software Foundation but are
+             moved into a
+                <a href="http://archive.apache.org/dist/commons/jexl/">
+             special archive area
+                </a>.
+            </p>
+        </section>
+    </body>
+</document>