Copybara import of the project:

  - 35f048cfae521189399a614f78a36c156f5e4c56 Initial contribution by Yamini K B <yamini.k.b@oracle.com>
  - 080692e83e4039403015e00c070f435b92e0aa27 Merge pull request #51 from ggam/master by Arjan Tijms <arjan.tijms@gmail.com>
  - 003e833ab0fd7eba1b30cb2a0fc066fc7bc3cd1f Merge pull request #53 from arjantijms/cleanup by Arjan Tijms <arjan.tijms@gmail.com>
  - 0334480ae40f514890ac2d48fec8c8176f35fa10 Merge pull request #54 from vinayvishal/remove-build-numb... by Arjan Tijms <arjan.tijms@gmail.com>
  - 72a825bedca1b11bc9bc4e4a88e8606f7bbfd1c2 Merge pull request #55 from vinayvishal/update-parent-pom... by Arjan Tijms <arjan.tijms@gmail.com>
  - 9147d0bcae7c850234815be4cc63c92ab8f0bd94 Merge pull request #56 from vinayvishal/add-required-plug... by Arjan Tijms <arjan.tijms@gmail.com>
  - 65e89aa76ed672761099ca4d2960fc6e246c0ca2 Merge pull request #59 from markt-asf/version-bump by Arjan Tijms <arjan.tijms@gmail.com>
  - dd440de92af3df5f3ba29fe594475671fe93c78d Merge pull request #62 from markt-asf/fix-extensionName by Arjan Tijms <arjan.tijms@gmail.com>
  - 56e45b79cd84d0227cd591bb49ec51aa0d0a2ead Merge pull request #60 from markt-asf/header-updates by Arjan Tijms <arjan.tijms@gmail.com>
  - a9f8a30143d96895ba6e653548cfcb2346aeb104 Merge pull request #66 from markt-asf/clean-up by Arjan Tijms <arjan.tijms@gmail.com>
  - 3ba2b58bab35c1758d81e2511c14ee36b40e1f65 Merge pull request #67 from Cousjava/updateparent by Arjan Tijms <arjan.tijms@gmail.com>
  - 21ff9158fb5bd21a8e74bf45554e4203689292ec Merge pull request #74 from markt-asf/master by Arjan Tijms <arjan.tijms@gmail.com>
  - fa0b884f1a66394c02f7610fe0a8d37ddfb5af32 Merge pull request #88 from arjantijms/master by Arjan Tijms <arjan.tijms@gmail.com>
  - 78b82726c2ea23186b890c1e2ece92b58624e85f Merge pull request #89 from arjantijms/master by Arjan Tijms <arjan.tijms@gmail.com>
  - 20965ed125f61696227064ab216d36486905f362 Merge pull request #90 from arjantijms/master by Arjan Tijms <arjan.tijms@gmail.com>
  - b3fcc1219c01e8404c72f579388bce3de167a6ec Merge pull request #91 from arjantijms/master by Arjan Tijms <arjan.tijms@gmail.com>
  - cf4da19f4f5eb441642d0c0a38dd20872d85a02d Merge pull request #92 from arjantijms/master by Arjan Tijms <arjan.tijms@gmail.com>
  - 50d69173777f83495dcf95b9e735f1d3355608ba Merge pull request #93 from arjantijms/master by Arjan Tijms <arjan.tijms@gmail.com>
  - 341b83102592b5d4a4c74c498000234e00f58cba Merge pull request #94 from arjantijms/master by Arjan Tijms <arjan.tijms@gmail.com>
  - 53ae0f7924140a08352401940ae9b4378871adb9 Merge pull request #95 from arjantijms/master by Arjan Tijms <arjan.tijms@gmail.com>
  - f9f0b3bfb4067c1db148e3dbd68da3df6615b90e Merge pull request #96 from arjantijms/master by Arjan Tijms <arjan.tijms@gmail.com>
  - 8bda97fefecb97eaead2b4a81968450bf1e0b34c Merge pull request #99 from scottmarlow/singnature by Arjan Tijms <arjan.tijms@gmail.com>
  - fcc0e1909a2fd9a84794c015237d27e85923df21 Merge pull request #100 from arjantijms/master by Arjan Tijms <arjan.tijms@gmail.com>
  - 04f3e5c8b6c4ea0123f868a184464ee6b87a6f5e Merge pull request #101 from arjantijms/master by Arjan Tijms <arjan.tijms@gmail.com>
  - 0a0b88253484cbb27d41a393f40526efd2c0c340 Merge pull request #102 from arjantijms/master by Arjan Tijms <arjan.tijms@gmail.com>
  - 2d97367b33159db1827c23390786b5d741dc781c Merge pull request #104 from arjantijms/master by Arjan Tijms <arjan.tijms@gmail.com>
  - 93021e5025868e79d606c3f4f20001db7fcff45e Prepare release org.glassfish:jakarta.el:3.0.3 by Eclipse EL-api Bot <el-bot@eclipse.org>
  - c76b9e688dc7731b84260400abfe8e785294d865 3x patch (#165) by Maxim Nesen <24524084+senivam@users.noreply.github.com>
  - 9be46eef3f619ccf380529ba12cafb9f23aea750 Prepare release org.glassfish:jakarta.el:3.0.4 by Eclipse EL Bot <el-bot@eclipse.org>

GitOrigin-RevId: 9be46eef3f619ccf380529ba12cafb9f23aea750
Change-Id: Ic5da60957c82f987950263992ca3e0687e9bc8fe
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..26c150a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,37 @@
+*.class
+.classpath
+.project
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.ear
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+.idea
+*.iml
+target
+
+# emacs
+*~
+
+*.class
+*.jar
+target/
+/bin/
+/dist/
+.settings/
+/**/.classpath
+/**/.project
+
+/.project
+nb-configuration.xml
+
+# mac
+.DS_Store
+
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..602c804
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,48 @@
+# Contributing to Jakarta Expression Language
+
+Thanks for your interest in this project.
+
+## Project description
+
+Jakarta Expression Language provides an important mechanism
+for enabling the presentation layer (web pages) to communicate with the
+application logic (managed beans). Jakarta Expression Language is used by several Jakarta EE
+technologies, such as Jakarta Faces, Jakarta Server Pages, Jakarta Security and Contexts and
+Dependency Injection for Jakarta EE (CDI). 
+
+Jakarta Expression Language can also be used in stand-alone environments.
+
+* https://projects.eclipse.org/projects/ee4j.el
+
+## Developer resources
+
+Information regarding source code management, builds, coding standards, and
+more.
+
+* https://projects.eclipse.org/projects/ee4j.el/developer
+
+The project maintains the following source code repositories
+
+* https://github.com/eclipse-ee4j/el-ri
+
+## Eclipse Contributor Agreement
+
+Before your contribution can be accepted by the project team contributors must
+electronically sign the Eclipse Contributor Agreement (ECA).
+
+* http://www.eclipse.org/legal/ECA.php
+
+Commits that are provided by non-committers must have a Signed-off-by field in
+the footer indicating that the author is aware of the terms by which the
+contribution has been provided to the project. The non-committer must
+additionally have an Eclipse Foundation account and must have a signed Eclipse
+Contributor Agreement (ECA) on file.
+
+For more information, please see the Eclipse Committer Handbook:
+https://www.eclipse.org/projects/handbook/#resources-commit
+
+## Contact
+
+Contact the project developers via the project's "dev" list.
+
+* 
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..5de3d1b
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,637 @@
+# Eclipse Public License - v 2.0
+
+        THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
+        PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
+        OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+    1. DEFINITIONS
+
+    "Contribution" means:
+
+      a) in the case of the initial Contributor, the initial content
+         Distributed under this Agreement, and
+
+      b) in the case of each subsequent Contributor: 
+         i) changes to the Program, and 
+         ii) additions to the Program;
+      where such changes and/or additions to the Program originate from
+      and are Distributed by that particular Contributor. A Contribution
+      "originates" from a Contributor if it was added to the Program by
+      such Contributor itself or anyone acting on such Contributor's behalf.
+      Contributions do not include changes or additions to the Program that
+      are not Modified Works.
+
+    "Contributor" means any person or entity that Distributes the Program.
+
+    "Licensed Patents" mean patent claims licensable by a Contributor which
+    are necessarily infringed by the use or sale of its Contribution alone
+    or when combined with the Program.
+
+    "Program" means the Contributions Distributed in accordance with this
+    Agreement.
+
+    "Recipient" means anyone who receives the Program under this Agreement
+    or any Secondary License (as applicable), including Contributors.
+
+    "Derivative Works" shall mean any work, whether in Source Code or other
+    form, that is based on (or derived from) the Program and for which the
+    editorial revisions, annotations, elaborations, or other modifications
+    represent, as a whole, an original work of authorship.
+
+    "Modified Works" shall mean any work in Source Code or other form that
+    results from an addition to, deletion from, or modification of the
+    contents of the Program, including, for purposes of clarity any new file
+    in Source Code form that contains any contents of the Program. Modified
+    Works shall not include works that contain only declarations,
+    interfaces, types, classes, structures, or files of the Program solely
+    in each case in order to link to, bind by name, or subclass the Program
+    or Modified Works thereof.
+
+    "Distribute" means the acts of a) distributing or b) making available
+    in any manner that enables the transfer of a copy.
+
+    "Source Code" means the form of a Program preferred for making
+    modifications, including but not limited to software source code,
+    documentation source, and configuration files.
+
+    "Secondary License" means either the GNU General Public License,
+    Version 2.0, or any later versions of that license, including any
+    exceptions or additional permissions as identified by the initial
+    Contributor.
+
+    2. GRANT OF RIGHTS
+
+      a) Subject to the terms of this Agreement, each Contributor hereby
+      grants Recipient a non-exclusive, worldwide, royalty-free copyright
+      license to reproduce, prepare Derivative Works of, publicly display,
+      publicly perform, Distribute and sublicense the Contribution of such
+      Contributor, if any, and such Derivative Works.
+
+      b) Subject to the terms of this Agreement, each Contributor hereby
+      grants Recipient a non-exclusive, worldwide, royalty-free patent
+      license under Licensed Patents to make, use, sell, offer to sell,
+      import and otherwise transfer the Contribution of such Contributor,
+      if any, in Source Code or other form. This patent license shall
+      apply to the combination of the Contribution and the Program if, at
+      the time the Contribution is added by the Contributor, such addition
+      of the Contribution causes such combination to be covered by the
+      Licensed Patents. The patent license shall not apply to any other
+      combinations which include the Contribution. No hardware per se is
+      licensed hereunder.
+
+      c) Recipient understands that although each Contributor grants the
+      licenses to its Contributions set forth herein, no assurances are
+      provided by any Contributor that the Program does not infringe the
+      patent or other intellectual property rights of any other entity.
+      Each Contributor disclaims any liability to Recipient for claims
+      brought by any other entity based on infringement of intellectual
+      property rights or otherwise. As a condition to exercising the
+      rights and licenses granted hereunder, each Recipient hereby
+      assumes sole responsibility to secure any other intellectual
+      property rights needed, if any. For example, if a third party
+      patent license is required to allow Recipient to Distribute the
+      Program, it is Recipient's responsibility to acquire that license
+      before distributing the Program.
+
+      d) Each Contributor represents that to its knowledge it has
+      sufficient copyright rights in its Contribution, if any, to grant
+      the copyright license set forth in this Agreement.
+
+      e) Notwithstanding the terms of any Secondary License, no
+      Contributor makes additional grants to any Recipient (other than
+      those set forth in this Agreement) as a result of such Recipient's
+      receipt of the Program under the terms of a Secondary License
+      (if permitted under the terms of Section 3).
+
+    3. REQUIREMENTS
+
+    3.1 If a Contributor Distributes the Program in any form, then:
+
+      a) the Program must also be made available as Source Code, in
+      accordance with section 3.2, and the Contributor must accompany
+      the Program with a statement that the Source Code for the Program
+      is available under this Agreement, and informs Recipients how to
+      obtain it in a reasonable manner on or through a medium customarily
+      used for software exchange; and
+
+      b) the Contributor may Distribute the Program under a license
+      different than this Agreement, provided that such license:
+         i) effectively disclaims on behalf of all other Contributors all
+         warranties and conditions, express and implied, including
+         warranties or conditions of title and non-infringement, and
+         implied warranties or conditions of merchantability and fitness
+         for a particular purpose;
+
+         ii) effectively excludes on behalf of all other Contributors all
+         liability for damages, including direct, indirect, special,
+         incidental and consequential damages, such as lost profits;
+
+         iii) does not attempt to limit or alter the recipients' rights
+         in the Source Code under section 3.2; and
+
+         iv) requires any subsequent distribution of the Program by any
+         party to be under a license that satisfies the requirements
+         of this section 3.
+
+    3.2 When the Program is Distributed as Source Code:
+
+      a) it must be made available under this Agreement, or if the
+      Program (i) is combined with other material in a separate file or
+      files made available under a Secondary License, and (ii) the initial
+      Contributor attached to the Source Code the notice described in
+      Exhibit A of this Agreement, then the Program may be made available
+      under the terms of such Secondary Licenses, and
+
+      b) a copy of this Agreement must be included with each copy of
+      the Program.
+
+    3.3 Contributors may not remove or alter any copyright, patent,
+    trademark, attribution notices, disclaimers of warranty, or limitations
+    of liability ("notices") contained within the Program from any copy of
+    the Program which they Distribute, provided that Contributors may add
+    their own appropriate notices.
+
+    4. COMMERCIAL DISTRIBUTION
+
+    Commercial distributors of software may accept certain responsibilities
+    with respect to end users, business partners and the like. While this
+    license is intended to facilitate the commercial use of the Program,
+    the Contributor who includes the Program in a commercial product
+    offering should do so in a manner which does not create potential
+    liability for other Contributors. Therefore, if a Contributor includes
+    the Program in a commercial product offering, such Contributor
+    ("Commercial Contributor") hereby agrees to defend and indemnify every
+    other Contributor ("Indemnified Contributor") against any losses,
+    damages and costs (collectively "Losses") arising from claims, lawsuits
+    and other legal actions brought by a third party against the Indemnified
+    Contributor to the extent caused by the acts or omissions of such
+    Commercial Contributor in connection with its distribution of the Program
+    in a commercial product offering. The obligations in this section do not
+    apply to any claims or Losses relating to any actual or alleged
+    intellectual property infringement. In order to qualify, an Indemnified
+    Contributor must: a) promptly notify the Commercial Contributor in
+    writing of such claim, and b) allow the Commercial Contributor to control,
+    and cooperate with the Commercial Contributor in, the defense and any
+    related settlement negotiations. The Indemnified Contributor may
+    participate in any such claim at its own expense.
+
+    For example, a Contributor might include the Program in a commercial
+    product offering, Product X. That Contributor is then a Commercial
+    Contributor. If that Commercial Contributor then makes performance
+    claims, or offers warranties related to Product X, those performance
+    claims and warranties are such Commercial Contributor's responsibility
+    alone. Under this section, the Commercial Contributor would have to
+    defend claims against the other Contributors related to those performance
+    claims and warranties, and if a court requires any other Contributor to
+    pay any damages as a result, the Commercial Contributor must pay
+    those damages.
+
+    5. NO WARRANTY
+
+    EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
+    PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED 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. Each Recipient is solely responsible for determining the
+    appropriateness of using and distributing the Program and assumes all
+    risks associated with its exercise of rights under this Agreement,
+    including but not limited to the risks and costs of program errors,
+    compliance with applicable laws, damage to or loss of data, programs
+    or equipment, and unavailability or interruption of operations.
+
+    6. DISCLAIMER OF LIABILITY
+
+    EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
+    PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS
+    SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
+    PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+    ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
+    EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
+    POSSIBILITY OF SUCH DAMAGES.
+
+    7. GENERAL
+
+    If any provision of this Agreement is invalid or unenforceable under
+    applicable law, it shall not affect the validity or enforceability of
+    the remainder of the terms of this Agreement, and without further
+    action by the parties hereto, such provision shall be reformed to the
+    minimum extent necessary to make such provision valid and enforceable.
+
+    If Recipient institutes patent litigation against any entity
+    (including a cross-claim or counterclaim in a lawsuit) alleging that the
+    Program itself (excluding combinations of the Program with other software
+    or hardware) infringes such Recipient's patent(s), then such Recipient's
+    rights granted under Section 2(b) shall terminate as of the date such
+    litigation is filed.
+
+    All Recipient's rights under this Agreement shall terminate if it
+    fails to comply with any of the material terms or conditions of this
+    Agreement and does not cure such failure in a reasonable period of
+    time after becoming aware of such noncompliance. If all Recipient's
+    rights under this Agreement terminate, Recipient agrees to cease use
+    and distribution of the Program as soon as reasonably practicable.
+    However, Recipient's obligations under this Agreement and any licenses
+    granted by Recipient relating to the Program shall continue and survive.
+
+    Everyone is permitted to copy and distribute copies of this Agreement,
+    but in order to avoid inconsistency the Agreement is copyrighted and
+    may only be modified in the following manner. The Agreement Steward
+    reserves the right to publish new versions (including revisions) of
+    this Agreement from time to time. No one other than the Agreement
+    Steward has the right to modify this Agreement. The Eclipse Foundation
+    is the initial Agreement Steward. The Eclipse Foundation may assign the
+    responsibility to serve as the Agreement Steward to a suitable separate
+    entity. Each new version of the Agreement will be given a distinguishing
+    version number. The Program (including Contributions) may always be
+    Distributed subject to the version of the Agreement under which it was
+    received. In addition, after a new version of the Agreement is published,
+    Contributor may elect to Distribute the Program (including its
+    Contributions) under the new version.
+
+    Except as expressly stated in Sections 2(a) and 2(b) above, Recipient
+    receives no rights or licenses to the intellectual property of any
+    Contributor under this Agreement, whether expressly, by implication,
+    estoppel or otherwise. All rights in the Program not expressly granted
+    under this Agreement are reserved. Nothing in this Agreement is intended
+    to be enforceable by any entity that is not a Contributor or Recipient.
+    No third-party beneficiary rights are created under this Agreement.
+
+    Exhibit A - Form of Secondary Licenses Notice
+
+    "This Source Code may also be made available under the following 
+    Secondary Licenses when the conditions for such availability set forth 
+    in the Eclipse Public License, v. 2.0 are satisfied: {name license(s),
+    version(s), and exceptions or additional permissions here}."
+
+      Simply including a copy of this Agreement, including this Exhibit A
+      is not sufficient to license the Source Code under Secondary Licenses.
+
+      If it is not possible or desirable to put the notice in a particular
+      file, then You may include the notice in a location (such as a LICENSE
+      file in a relevant directory) where a recipient would be likely to
+      look for such a notice.
+
+      You may add additional accurate notices of copyright ownership.
+
+---
+
+##    The GNU General Public License (GPL) Version 2, June 1991
+
+    Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+    51 Franklin Street, Fifth Floor
+    Boston, MA 02110-1335
+    USA
+
+    Everyone is permitted to copy and distribute verbatim copies
+    of this license document, but changing it is not allowed.
+
+    Preamble
+
+    The licenses for most software are designed to take away your freedom to
+    share and change it. By contrast, the GNU General Public License is
+    intended to guarantee your freedom to share and change free software--to
+    make sure the software is free for all its users. This General Public
+    License applies to most of the Free Software Foundation's software and
+    to any other program whose authors commit to using it. (Some other Free
+    Software Foundation software is covered by the GNU Library General
+    Public License instead.) You can apply it to your programs, too.
+
+    When we speak of free software, we are referring to freedom, not price.
+    Our General Public Licenses are designed to make sure that you have the
+    freedom to distribute copies of free software (and charge for this
+    service if you wish), that you receive source code or can get it if you
+    want it, that you can change the software or use pieces of it in new
+    free programs; and that you know you can do these things.
+
+    To protect your rights, we need to make restrictions that forbid anyone
+    to deny you these rights or to ask you to surrender the rights. These
+    restrictions translate to certain responsibilities for you if you
+    distribute copies of the software, or if you modify it.
+
+    For example, if you distribute copies of such a program, whether gratis
+    or for a fee, you must give the recipients all the rights that you have.
+    You must make sure that they, too, receive or can get the source code.
+    And you must show them these terms so they know their rights.
+
+    We protect your rights with two steps: (1) copyright the software, and
+    (2) offer you this license which gives you legal permission to copy,
+    distribute and/or modify the software.
+
+    Also, for each author's protection and ours, we want to make certain
+    that everyone understands that there is no warranty for this free
+    software. If the software is modified by someone else and passed on, we
+    want its recipients to know that what they have is not the original, so
+    that any problems introduced by others will not reflect on the original
+    authors' reputations.
+
+    Finally, any free program is threatened constantly by software patents.
+    We wish to avoid the danger that redistributors of a free program will
+    individually obtain patent licenses, in effect making the program
+    proprietary. To prevent this, we have made it clear that any patent must
+    be licensed for everyone's free use or not licensed at all.
+
+    The precise terms and conditions for copying, distribution and
+    modification follow.
+
+    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+    0. This License applies to any program or other work which contains a
+    notice placed by the copyright holder saying it may be distributed under
+    the terms of this General Public License. The "Program", below, refers
+    to any such program or work, and a "work based on the Program" means
+    either the Program or any derivative work under copyright law: that is
+    to say, a work containing the Program or a portion of it, either
+    verbatim or with modifications and/or translated into another language.
+    (Hereinafter, translation is included without limitation in the term
+    "modification".) Each licensee is addressed as "you".
+
+    Activities other than copying, distribution and modification are not
+    covered by this License; they are outside its scope. The act of running
+    the Program is not restricted, and the output from the Program is
+    covered only if its contents constitute a work based on the Program
+    (independent of having been made by running the Program). Whether that
+    is true depends on what the Program does.
+
+    1. You may copy and distribute verbatim copies of the Program's source
+    code as you receive it, in any medium, provided that you conspicuously
+    and appropriately publish on each copy an appropriate copyright notice
+    and disclaimer of warranty; keep intact all the notices that refer to
+    this License and to the absence of any warranty; and give any other
+    recipients of the Program a copy of this License along with the Program.
+
+    You may charge a fee for the physical act of transferring a copy, and
+    you may at your option offer warranty protection in exchange for a fee.
+
+    2. You may modify your copy or copies of the Program or any portion of
+    it, thus forming a work based on the Program, and copy and distribute
+    such modifications or work under the terms of Section 1 above, provided
+    that you also meet all of these conditions:
+
+        a) You must cause the modified files to carry prominent notices
+        stating that you changed the files and the date of any change.
+
+        b) You must cause any work that you distribute or publish, that in
+        whole or in part contains or is derived from the Program or any part
+        thereof, to be licensed as a whole at no charge to all third parties
+        under the terms of this License.
+
+        c) If the modified program normally reads commands interactively
+        when run, you must cause it, when started running for such
+        interactive use in the most ordinary way, to print or display an
+        announcement including an appropriate copyright notice and a notice
+        that there is no warranty (or else, saying that you provide a
+        warranty) and that users may redistribute the program under these
+        conditions, and telling the user how to view a copy of this License.
+        (Exception: if the Program itself is interactive but does not
+        normally print such an announcement, your work based on the Program
+        is not required to print an announcement.)
+
+    These requirements apply to the modified work as a whole. If
+    identifiable sections of that work are not derived from the Program, and
+    can be reasonably considered independent and separate works in
+    themselves, then this License, and its terms, do not apply to those
+    sections when you distribute them as separate works. But when you
+    distribute the same sections as part of a whole which is a work based on
+    the Program, the distribution of the whole must be on the terms of this
+    License, whose permissions for other licensees extend to the entire
+    whole, and thus to each and every part regardless of who wrote it.
+
+    Thus, it is not the intent of this section to claim rights or contest
+    your rights to work written entirely by you; rather, the intent is to
+    exercise the right to control the distribution of derivative or
+    collective works based on the Program.
+
+    In addition, mere aggregation of another work not based on the Program
+    with the Program (or with a work based on the Program) on a volume of a
+    storage or distribution medium does not bring the other work under the
+    scope of this License.
+
+    3. You may copy and distribute the Program (or a work based on it,
+    under Section 2) in object code or executable form under the terms of
+    Sections 1 and 2 above provided that you also do one of the following:
+
+        a) Accompany it with the complete corresponding machine-readable
+        source code, which must be distributed under the terms of Sections 1
+        and 2 above on a medium customarily used for software interchange; or,
+
+        b) Accompany it with a written offer, valid for at least three
+        years, to give any third party, for a charge no more than your cost
+        of physically performing source distribution, a complete
+        machine-readable copy of the corresponding source code, to be
+        distributed under the terms of Sections 1 and 2 above on a medium
+        customarily used for software interchange; or,
+
+        c) Accompany it with the information you received as to the offer to
+        distribute corresponding source code. (This alternative is allowed
+        only for noncommercial distribution and only if you received the
+        program in object code or executable form with such an offer, in
+        accord with Subsection b above.)
+
+    The source code for a work means the preferred form of the work for
+    making modifications to it. For an executable work, complete source code
+    means all the source code for all modules it contains, plus any
+    associated interface definition files, plus the scripts used to control
+    compilation and installation of the executable. However, as a special
+    exception, the source code distributed need not include anything that is
+    normally distributed (in either source or binary form) with the major
+    components (compiler, kernel, and so on) of the operating system on
+    which the executable runs, unless that component itself accompanies the
+    executable.
+
+    If distribution of executable or object code is made by offering access
+    to copy from a designated place, then offering equivalent access to copy
+    the source code from the same place counts as distribution of the source
+    code, even though third parties are not compelled to copy the source
+    along with the object code.
+
+    4. You may not copy, modify, sublicense, or distribute the Program
+    except as expressly provided under this License. Any attempt otherwise
+    to copy, modify, sublicense or distribute the Program is void, and will
+    automatically terminate your rights under this License. However, parties
+    who have received copies, or rights, from you under this License will
+    not have their licenses terminated so long as such parties remain in
+    full compliance.
+
+    5. You are not required to accept this License, since you have not
+    signed it. However, nothing else grants you permission to modify or
+    distribute the Program or its derivative works. These actions are
+    prohibited by law if you do not accept this License. Therefore, by
+    modifying or distributing the Program (or any work based on the
+    Program), you indicate your acceptance of this License to do so, and all
+    its terms and conditions for copying, distributing or modifying the
+    Program or works based on it.
+
+    6. Each time you redistribute the Program (or any work based on the
+    Program), the recipient automatically receives a license from the
+    original licensor to copy, distribute or modify the Program subject to
+    these terms and conditions. You may not impose any further restrictions
+    on the recipients' exercise of the rights granted herein. You are not
+    responsible for enforcing compliance by third parties to this License.
+
+    7. If, as a consequence of a court judgment or allegation of patent
+    infringement or for any other reason (not limited to patent issues),
+    conditions are imposed on you (whether by court order, agreement or
+    otherwise) that contradict the conditions of this License, they do not
+    excuse you from the conditions of this License. If you cannot distribute
+    so as to satisfy simultaneously your obligations under this License and
+    any other pertinent obligations, then as a consequence you may not
+    distribute the Program at all. For example, if a patent license would
+    not permit royalty-free redistribution of the Program by all those who
+    receive copies directly or indirectly through you, then the only way you
+    could satisfy both it and this License would be to refrain entirely from
+    distribution of the Program.
+
+    If any portion of this section is held invalid or unenforceable under
+    any particular circumstance, the balance of the section is intended to
+    apply and the section as a whole is intended to apply in other
+    circumstances.
+
+    It is not the purpose of this section to induce you to infringe any
+    patents or other property right claims or to contest validity of any
+    such claims; this section has the sole purpose of protecting the
+    integrity of the free software distribution system, which is implemented
+    by public license practices. Many people have made generous
+    contributions to the wide range of software distributed through that
+    system in reliance on consistent application of that system; it is up to
+    the author/donor to decide if he or she is willing to distribute
+    software through any other system and a licensee cannot impose that choice.
+
+    This section is intended to make thoroughly clear what is believed to be
+    a consequence of the rest of this License.
+
+    8. If the distribution and/or use of the Program is restricted in
+    certain countries either by patents or by copyrighted interfaces, the
+    original copyright holder who places the Program under this License may
+    add an explicit geographical distribution limitation excluding those
+    countries, so that distribution is permitted only in or among countries
+    not thus excluded. In such case, this License incorporates the
+    limitation as if written in the body of this License.
+
+    9. The Free Software Foundation may publish revised and/or new
+    versions of the General Public License from time to time. Such new
+    versions will be similar in spirit to the present version, but may
+    differ in detail to address new problems or concerns.
+
+    Each version is given a distinguishing version number. If the Program
+    specifies a version number of this License which applies to it and "any
+    later version", you have the option of following the terms and
+    conditions either of that version or of any later version published by
+    the Free Software Foundation. If the Program does not specify a version
+    number of this License, you may choose any version ever published by the
+    Free Software Foundation.
+
+    10. If you wish to incorporate parts of the Program into other free
+    programs whose distribution conditions are different, write to the
+    author to ask for permission. For software which is copyrighted by the
+    Free Software Foundation, write to the Free Software Foundation; we
+    sometimes make exceptions for this. Our decision will be guided by the
+    two goals of preserving the free status of all derivatives of our free
+    software and of promoting the sharing and reuse of software generally.
+
+    NO WARRANTY
+
+    11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO
+    WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+    EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+    OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND,
+    EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+    ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH
+    YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+    NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+    12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+    WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+    AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR
+    DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL
+    DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM
+    (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
+    INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF
+    THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR
+    OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+    END OF TERMS AND CONDITIONS
+
+    How to Apply These Terms to Your New Programs
+
+    If you develop a new program, and you want it to be of the greatest
+    possible use to the public, the best way to achieve this is to make it
+    free software which everyone can redistribute and change under these terms.
+
+    To do so, attach the following notices to the program. It is safest to
+    attach them to the start of each source file to most effectively convey
+    the exclusion of warranty; and each file should have at least the
+    "copyright" line and a pointer to where the full notice is found.
+
+        One line to give the program's name and a brief idea of what it does.
+        Copyright (C) <year> <name of author>
+
+        This program is free software; you can redistribute it and/or modify
+        it under the terms of the GNU General Public License as published by
+        the Free Software Foundation; either version 2 of the License, or
+        (at your option) any later version.
+
+        This program is distributed in the hope that it will be useful, but
+        WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+        General Public License for more details.
+
+        You should have received a copy of the GNU General Public License
+        along with this program; if not, write to the Free Software
+        Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
+
+    Also add information on how to contact you by electronic and paper mail.
+
+    If the program is interactive, make it output a short notice like this
+    when it starts in an interactive mode:
+
+        Gnomovision version 69, Copyright (C) year name of author
+        Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type
+        `show w'. This is free software, and you are welcome to redistribute
+        it under certain conditions; type `show c' for details.
+
+    The hypothetical commands `show w' and `show c' should show the
+    appropriate parts of the General Public License. Of course, the commands
+    you use may be called something other than `show w' and `show c'; they
+    could even be mouse-clicks or menu items--whatever suits your program.
+
+    You should also get your employer (if you work as a programmer) or your
+    school, if any, to sign a "copyright disclaimer" for the program, if
+    necessary. Here is a sample; alter the names:
+
+        Yoyodyne, Inc., hereby disclaims all copyright interest in the
+        program `Gnomovision' (which makes passes at compilers) written by
+        James Hacker.
+
+        signature of Ty Coon, 1 April 1989
+        Ty Coon, President of Vice
+
+    This General Public License does not permit incorporating your program
+    into proprietary programs. If your program is a subroutine library, you
+    may consider it more useful to permit linking proprietary applications
+    with the library. If this is what you want to do, use the GNU Library
+    General Public License instead of this License.
+
+---
+
+## CLASSPATH EXCEPTION
+
+    Linking this library statically or dynamically with other modules is
+    making a combined work based on this library.  Thus, the terms and
+    conditions of the GNU General Public License version 2 cover the whole
+    combination.
+
+    As a special exception, the copyright holders of this library give you
+    permission to link this library with independent modules to produce an
+    executable, regardless of the license terms of these independent
+    modules, and to copy and distribute the resulting executable under
+    terms of your choice, provided that you also meet, for each linked
+    independent module, the terms and conditions of the license of that
+    module.  An independent module is a module which is not derived from or
+    based on this library.  If you modify this library, you may extend this
+    exception to your version of the library, but you are not obligated to
+    do so.  If you do not wish to do so, delete this exception statement
+    from your version.
diff --git a/NOTICE.md b/NOTICE.md
new file mode 100644
index 0000000..9e121fd
--- /dev/null
+++ b/NOTICE.md
@@ -0,0 +1,45 @@
+# Notices for Jakarta Expression Language
+
+This content is produced and maintained by the Jakarta Expression Language project.
+
+* Project home: https://projects.eclipse.org/projects/ee4j.el
+
+## Trademarks
+
+Jakarta Expression Language is a trademark of the Eclipse
+Foundation.
+
+## Copyright
+
+All content is the property of the respective authors or their employers. For
+more information regarding authorship of content, please consult the listed
+source code repository logs.
+
+## Declared Project Licenses
+
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License v. 2.0 which is available at
+http://www.eclipse.org/legal/epl-2.0. This Source Code may also be made
+available under the following Secondary Licenses when the conditions for such
+availability set forth in the Eclipse Public License v. 2.0 are satisfied: GNU
+General Public License, version 2 with the GNU Classpath Exception which is
+available at https://www.gnu.org/software/classpath/license.html.
+
+SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+## Source Code
+
+The project maintains the following source code repositories:
+
+* https://github.com/eclipse-ee4j/el-ri
+
+## Third-party Content
+
+## Cryptography
+
+Content may contain encryption software. The country in which you are currently
+may have restrictions on the import, possession, and use, and/or re-export to
+another country, of encryption software. BEFORE using any encryption software,
+please check the country's laws, regulations and policies concerning the import,
+possession, or use, and re-export of encryption software, to see if this is
+permitted.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8e1b614
--- /dev/null
+++ b/README.md
@@ -0,0 +1,24 @@
+# Jakarta Expression Language
+
+This repository contains the code for Jakarta Expression Language.
+
+[Online JavaDoc](https://javadoc.io/doc/jakarta.el/jakarta.el-api/)
+
+Building
+--------
+
+Jakarta Authentication can be built by executing the following from the project root:
+
+``mvn clean package``
+
+The API jar can then be found in /target.
+
+Making Changes
+--------------
+
+To make changes, fork this repository, make your changes, and submit a pull request.
+
+About Jakarta Expression Language
+-------------
+
+Jakarta Expression Language defines an expression language for Java applications.
\ No newline at end of file
diff --git a/api/pom.xml b/api/pom.xml
new file mode 100644
index 0000000..e713eaa
--- /dev/null
+++ b/api/pom.xml
@@ -0,0 +1,286 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 1997, 2018 Oracle and/or its affiliates and others.
+    All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    
+    <modelVersion>4.0.0</modelVersion>
+    
+    <parent>
+        <groupId>org.eclipse.ee4j</groupId>
+        <artifactId>project</artifactId>
+        <version>1.0.5</version>
+        <relativePath/>
+    </parent>
+    
+    <groupId>jakarta.el</groupId>
+    <artifactId>jakarta.el-api</artifactId>
+    <version>3.0.3-SNAPSHOT</version>
+    
+    <name>Jakarta Expression Language 3.0 API</name>
+    <description>
+        Jakarta Expression Language defines an expression language for Java applications
+    </description>
+    <url>https://projects.eclipse.org/projects/ee4j.el</url>
+
+    <properties>
+        <!-- Make sure the two versions are in sync with the maven version -->
+        <spec.version>3.0</spec.version>
+        <bundle.version>3.0.3</bundle.version>
+        <extensionName>javax.el</extensionName>
+        <bundle.symbolicName>javax.el-api</bundle.symbolicName>
+        <vendorName>Oracle Corporation</vendorName>
+        <findbugs.version>2.4.0</findbugs.version>
+        <findbugs.exclude />
+        <findbugs.threshold>High</findbugs.threshold>
+    </properties>
+
+    <licenses>
+        <license>
+            <name>EPL 2.0</name>
+            <url>http://www.eclipse.org/legal/epl-2.0</url>
+            <distribution>repo</distribution>
+        </license>
+        <license>
+            <name>GPL2 w/ CPE</name>
+            <url>https://www.gnu.org/software/classpath/license.html</url>
+            <distribution>repo</distribution>
+        </license>
+    </licenses>
+    
+    <issueManagement>
+        <system>github</system>
+        <url>https://github.com/eclipse-ee4j/el-ri/issues</url>
+    </issueManagement>
+    
+    <mailingLists>
+        <mailingList>
+            <name>Jakarta Expression Language mailing list</name>
+            <post>el-dev@eclipse.org</post>
+            <subscribe>https://dev.eclipse.org/mailman/listinfo/el-dev</subscribe>
+            <unsubscribe>https://dev.eclipse.org/mailman/listinfo/el-dev</unsubscribe>
+            <archive>https://dev.eclipse.org/mhonarc/lists/el-dev</archive>
+        </mailingList>
+    </mailingLists>
+    
+    <scm>
+        <connection>scm:git:https://github.com/eclipse-ee4j/el-ri.git</connection>
+        <developerConnection>scm:git:git@github.com:eclipse-ee4j/el-ri.git</developerConnection>
+        <url>https://github.com/eclipse-ee4j/el-ri</url>
+        <tag>HEAD</tag>
+    </scm>
+
+    <developers>
+        <developer>
+            <id>yaminikb</id>
+            <name>Yamini K B</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+        </developer>
+    </developers>
+
+    <contributors>
+        <contributor>
+            <name>Kin-man Chung</name>
+        </contributor>
+    </contributors>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>src/main/java</directory>
+                <includes>
+                    <include>**/*.properties</include>
+                </includes>
+            </resource>
+            <resource>
+                <directory>${project.basedir}/..</directory>
+                <includes>
+                    <include>LICENSE.md</include>
+                    <include>NOTICE.md</include>
+                </includes>
+                <targetPath>META-INF</targetPath>
+            </resource>
+        </resources>
+    
+        <plugins>
+             <!-- Restricts the Java version to 1.7 -->
+             <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.0</version>
+                <configuration>
+                    <source>1.7</source>
+                    <target>1.7</target>
+                    <compilerArgument>-Xlint:unchecked</compilerArgument>
+                </configuration>
+            </plugin>
+        
+        
+            <!-- Creates the OSGi MANIFEST.MF file -->        
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <version>1.4.3</version>
+                <configuration>
+                    <supportedProjectTypes>
+                        <supportedProjectType>jar</supportedProjectType>
+                    </supportedProjectTypes>
+                    <instructions>
+                        <Bundle-Description>Jakarta Expression Language ${spec.version}</Bundle-Description>
+                        <Bundle-SymbolicName>${bundle.symbolicName}</Bundle-SymbolicName>
+                        <Bundle-Version>${bundle.version}</Bundle-Version>
+                        <Extension-Name>${extensionName}</Extension-Name>
+                        <Specification-Version>${spec.version}</Specification-Version>
+                        <Specification-Vendor>${vendorName}</Specification-Vendor>
+                        <Implementation-Version>${project.version}</Implementation-Version>
+                        <Implementation-Vendor>${vendorName}</Implementation-Vendor>
+                        <Export-Package>javax.el</Export-Package>
+                    </instructions>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>bundle-manifest</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>manifest</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            
+            <!-- Adds the manifest file created by the org.apache.felix:maven-bundle-plugin -->
+            <plugin>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>2.4</version>
+                <configuration>
+                    <archive>
+                        <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+                    </archive>
+                    <excludes>
+                        <exclude>**/*.java</exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+            
+            <!-- Creates the source jar -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-source-plugin</artifactId>
+                <version>3.0.1</version>
+                <configuration>
+                    <includePom>true</includePom>
+                </configuration>
+                <executions>
+                    <execution>
+                       <id>attach-sources</id>
+                       <goals>
+                           <goal>jar-no-fork</goal> 
+                       </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            
+             <!-- 
+               Create Javadoc for API jar
+             -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>3.1.1</version>
+                <executions>
+                    <execution>
+                        <id>attach-api-javadocs</id>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                        <configuration>
+                            <source>1.8</source>
+                            <additionalJOption>-Xdoclint:none</additionalJOption>
+                            <description>Jakarta Expression Language API documentation</description>
+                            <doctitle>Jakarta Expression Language API documentation</doctitle>
+                            <windowtitle>Jakarta Expression Language API documentation</windowtitle>
+                            <header><![CDATA[<br>Jakarta Expression Language API v${project.version}]]></header>
+                            <bottom><![CDATA[
+Comments to: <a href="mailto:el-dev@eclipse.org">el-dev@eclipse.org</a>.<br>
+Copyright &#169; 2019 Eclipse Foundation. All rights reserved.<br>
+Use is subject to <a href="{@docRoot}/doc-files/speclicense.html" target="_top">license terms</a>.]]>
+                            </bottom>
+                            <docfilessubdirs>true</docfilessubdirs>
+                            <groups>
+                                <group>
+                                    <title>Jakarta Expression Language API Documentation</title>
+                                    <packages>
+                                        javax.el.*
+                                    </packages>
+                                </group>
+                            </groups>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>findbugs-maven-plugin</artifactId>
+                <version>${findbugs.version}</version>
+                <configuration>
+                    <threshold>${findbugs.threshold}</threshold>
+                    <excludeFilterFile>${findbugs.exclude}</excludeFilterFile>
+                    <findbugsXmlOutput>true</findbugsXmlOutput>
+                    <findbugsXmlWithMessages>true</findbugsXmlWithMessages>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-release-plugin</artifactId>
+                <configuration>
+                    <mavenExecutorId>forked-path</mavenExecutorId>
+                    <useReleaseProfile>false</useReleaseProfile>
+                    <arguments>${release.arguments}</arguments>
+                </configuration>
+            </plugin>
+            
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-enforcer-plugin</artifactId>
+                <version>3.0.0-M2</version>
+            </plugin>               
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-gpg-plugin</artifactId>
+                <version>1.6</version>          
+            </plugin>
+        </plugins>
+    </build>
+
+    <reporting>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>findbugs-maven-plugin</artifactId>
+                <version>${findbugs.version}</version>
+                <configuration>
+                    <threshold>${findbugs.threshold}</threshold>
+                    <excludeFilterFile>${findbugs.exclude}</excludeFilterFile>
+                </configuration>
+            </plugin>
+        </plugins>
+    </reporting>
+</project>
diff --git a/api/src/main/java/javax/el/ArrayELResolver.java b/api/src/main/java/javax/el/ArrayELResolver.java
new file mode 100644
index 0000000..aa088f5
--- /dev/null
+++ b/api/src/main/java/javax/el/ArrayELResolver.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package javax.el;
+
+import java.beans.FeatureDescriptor;
+import java.lang.reflect.Array;
+import java.util.Iterator;
+
+/**
+ * Defines property resolution behavior on arrays.
+ *
+ * <p>
+ * This resolver handles base objects that are Java language arrays. It accepts any object as a property and coerces
+ * that object into an integer index into the array. The resulting value is the value in the array at that index.
+ * </p>
+ *
+ * <p>
+ * This resolver can be constructed in read-only mode, which means that {@link #isReadOnly} will always return
+ * <code>true</code> and {@link #setValue} will always throw <code>PropertyNotWritableException</code>.
+ * </p>
+ *
+ * <p>
+ * <code>ELResolver</code>s are combined together using {@link CompositeELResolver}s, to define rich semantics for
+ * evaluating an expression. See the javadocs for {@link ELResolver} for details.
+ * </p>
+ *
+ * @see CompositeELResolver
+ * @see ELResolver
+ * 
+ * @since Jakarta Server Pages 2.1
+ */
+public class ArrayELResolver extends ELResolver {
+
+    /**
+     * Creates a new read/write <code>ArrayELResolver</code>.
+     */
+    public ArrayELResolver() {
+        this.isReadOnly = false;
+    }
+
+    /**
+     * Creates a new <code>ArrayELResolver</code> whose read-only status is determined by the given parameter.
+     *
+     * @param isReadOnly <code>true</code> if this resolver cannot modify arrays; <code>false</code> otherwise.
+     */
+    public ArrayELResolver(boolean isReadOnly) {
+        this.isReadOnly = isReadOnly;
+    }
+
+    /**
+     * If the base object is an array, returns the most general acceptable type for a value in this array.
+     *
+     * <p>
+     * If the base is a <code>array</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object
+     * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after
+     * this method is called, the caller should ignore the return value.
+     * </p>
+     *
+     * <p>
+     * Assuming the base is an <code>array</code>, this method will always return
+     * <code>base.getClass().getComponentType()</code>, which is the most general type of component that can be stored at
+     * any given index in the array.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The array to analyze. Only bases that are Java language arrays are handled by this resolver.
+     * @param property The index of the element in the array to return the acceptable type for. Will be coerced into an
+     * integer, but otherwise ignored by this resolver.
+     * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
+     * the most general acceptable type; otherwise undefined.
+     * @throws PropertyNotFoundException if the given index is out of bounds for this array.
+     * @throws NullPointerException if context is <code>null</code>
+     * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     */
+    @Override
+    public Class<?> getType(ELContext context, Object base, Object property) {
+
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base != null && base.getClass().isArray()) {
+            context.setPropertyResolved(true);
+            int index = toInteger(property);
+            if (index < 0 || index >= Array.getLength(base)) {
+                throw new PropertyNotFoundException();
+            }
+            return base.getClass().getComponentType();
+        }
+        return null;
+    }
+
+    /**
+     * If the base object is a Java language array, returns the value at the given index. The index is specified by the
+     * <code>property</code> argument, and coerced into an integer. If the coercion could not be performed, an
+     * <code>IllegalArgumentException</code> is thrown. If the index is out of bounds, <code>null</code> is returned.
+     *
+     * <p>
+     * If the base is a Java language array, the <code>propertyResolved</code> property of the <code>ELContext</code> object
+     * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after
+     * this method is called, the caller should ignore the return value.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The array to analyze. Only bases that are Java language arrays are handled by this resolver.
+     * @param property The index of the value to be returned. Will be coerced into an integer.
+     * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
+     * the value at the given index or <code>null</code> if the index was out of bounds. Otherwise, undefined.
+     * @throws IllegalArgumentException if the property could not be coerced into an integer.
+     * @throws NullPointerException if context is <code>null</code>.
+     * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     */
+    @Override
+    public Object getValue(ELContext context, Object base, Object property) {
+
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base != null && base.getClass().isArray()) {
+            context.setPropertyResolved(base, property);
+            int index = toInteger(property);
+            if (index >= 0 && index < Array.getLength(base)) {
+                return Array.get(base, index);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * If the base object is a Java language array, attempts to set the value at the given index with the given value. The
+     * index is specified by the <code>property</code> argument, and coerced into an integer. If the coercion could not be
+     * performed, an <code>IllegalArgumentException</code> is thrown. If the index is out of bounds, a
+     * <code>PropertyNotFoundException</code> is thrown.
+     *
+     * <p>
+     * If the base is a Java language array, the <code>propertyResolved</code> property of the <code>ELContext</code> object
+     * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after
+     * this method is called, the caller can safely assume no value was set.
+     * </p>
+     *
+     * <p>
+     * If this resolver was constructed in read-only mode, this method will always throw
+     * <code>PropertyNotWritableException</code>.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The array to be modified. Only bases that are Java language arrays are handled by this resolver.
+     * @param property The index of the value to be set. Will be coerced into an integer.
+     * @param val The value to be set at the given index.
+     * @throws ClassCastException if the class of the specified element prevents it from being added to this array.
+     * @throws NullPointerException if context is <code>null</code>.
+     * @throws IllegalArgumentException if the property could not be coerced into an integer, or if some aspect of the
+     * specified element prevents it from being added to this array.
+     * @throws PropertyNotWritableException if this resolver was constructed in read-only mode.
+     * @throws PropertyNotFoundException if the given index is out of bounds for this array.
+     * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     */
+    @Override
+    public void setValue(ELContext context, Object base, Object property, Object val) {
+
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base != null && base.getClass().isArray()) {
+            context.setPropertyResolved(base, property);
+            if (isReadOnly) {
+                throw new PropertyNotWritableException();
+            }
+            Class<?> type = base.getClass().getComponentType();
+            if (val != null && !type.isAssignableFrom(val.getClass())) {
+                throw new ClassCastException();
+            }
+            int index = toInteger(property);
+            if (index < 0 || index >= Array.getLength(base)) {
+                throw new PropertyNotFoundException();
+            }
+            Array.set(base, index, val);
+        }
+    }
+
+    /**
+     * If the base object is a Java language array, returns whether a call to {@link #setValue} will always fail.
+     *
+     * <p>
+     * If the base is a Java language array, the <code>propertyResolved</code> property of the <code>ELContext</code> object
+     * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after
+     * this method is called, the caller should ignore the return value.
+     * </p>
+     *
+     * <p>
+     * If this resolver was constructed in read-only mode, this method will always return <code>true</code>. Otherwise, it
+     * returns <code>false</code>.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The array to analyze. Only bases that are a Java language array are handled by this resolver.
+     * @param property The index of the element in the array to return the acceptable type for. Will be coerced into an
+     * integer, but otherwise ignored by this resolver.
+     * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
+     * <code>true</code> if calling the <code>setValue</code> method will always fail or <code>false</code> if it is
+     * possible that such a call may succeed; otherwise undefined.
+     * @throws PropertyNotFoundException if the given index is out of bounds for this array.
+     * @throws NullPointerException if context is <code>null</code>
+     * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     */
+    @Override
+    public boolean isReadOnly(ELContext context, Object base, Object property) {
+
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base != null && base.getClass().isArray()) {
+            context.setPropertyResolved(true);
+            int index = toInteger(property);
+            if (index < 0 || index >= Array.getLength(base)) {
+                throw new PropertyNotFoundException();
+            }
+        }
+        return isReadOnly;
+    }
+
+    /**
+     * Always returns <code>null</code>, since there is no reason to iterate through set set of all integers.
+     *
+     * <p>
+     * The {@link #getCommonPropertyType} method returns sufficient information about what properties this resolver accepts.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The array to analyze. Only bases that are a Java language array are handled by this resolver.
+     * @return <code>null</code>.
+     */
+    @Override
+    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
+        return null;
+    }
+
+    /**
+     * If the base object is a Java language array, returns the most general type that this resolver accepts for the
+     * <code>property</code> argument. Otherwise, returns <code>null</code>.
+     *
+     * <p>
+     * Assuming the base is an array, this method will always return <code>Integer.class</code>. This is because arrays
+     * accept integers for their index.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The array to analyze. Only bases that are a Java language array are handled by this resolver.
+     * @return <code>null</code> if base is not a Java language array; otherwise <code>Integer.class</code>.
+     */
+    @Override
+    public Class<?> getCommonPropertyType(ELContext context, Object base) {
+
+        if (base != null && base.getClass().isArray()) {
+            return Integer.class;
+        }
+        return null;
+    }
+
+    private int toInteger(Object p) {
+
+        if (p instanceof Integer) {
+            return ((Integer) p).intValue();
+        }
+        if (p instanceof Character) {
+            return ((Character) p).charValue();
+        }
+        if (p instanceof Boolean) {
+            return ((Boolean) p).booleanValue() ? 1 : 0;
+        }
+        if (p instanceof Number) {
+            return ((Number) p).intValue();
+        }
+        if (p instanceof String) {
+            return Integer.parseInt((String) p);
+        }
+        throw new IllegalArgumentException();
+    }
+
+    private boolean isReadOnly;
+}
diff --git a/api/src/main/java/javax/el/BeanELResolver.java b/api/src/main/java/javax/el/BeanELResolver.java
new file mode 100644
index 0000000..2ffd50d
--- /dev/null
+++ b/api/src/main/java/javax/el/BeanELResolver.java
@@ -0,0 +1,581 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package javax.el;
+
+import static java.lang.Boolean.TRUE;
+import static javax.el.ELUtil.getExceptionMessageString;
+
+import java.beans.BeanInfo;
+import java.beans.FeatureDescriptor;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Defines property resolution behavior on objects using the JavaBeans component architecture.
+ *
+ * <p>
+ * This resolver handles base objects of any type, as long as the base is not <code>null</code>. It accepts any object
+ * as a property or method, and coerces it to a string.
+ *
+ * <p>
+ * For property resolution, the property string is used to find a JavaBeans compliant property on the base object. The
+ * value is accessed using JavaBeans getters and setters.
+ * </p>
+ *
+ * <p>
+ * For method resolution, the method string is the name of the method in the bean. The parameter types can be optionally
+ * specified to identify the method. If the parameter types are not specified, the parameter objects are used in the
+ * method resolution.
+ * </p>
+ *
+ * <p>
+ * This resolver can be constructed in read-only mode, which means that {@link #isReadOnly} will always return
+ * <code>true</code> and {@link #setValue} will always throw <code>PropertyNotWritableException</code>.
+ * </p>
+ *
+ * <p>
+ * <code>ELResolver</code>s are combined together using {@link CompositeELResolver}s, to define rich semantics for
+ * evaluating an expression. See the javadocs for {@link ELResolver} for details.
+ * </p>
+ *
+ * <p>
+ * Because this resolver handles base objects of any type, it should be placed near the end of a composite resolver.
+ * Otherwise, it will claim to have resolved a property before any resolvers that come after it get a chance to test if
+ * they can do so as well.
+ * </p>
+ *
+ * @see CompositeELResolver
+ * @see ELResolver
+ * 
+ * @since Jakarta Server Pages 2.1
+ */
+public class BeanELResolver extends ELResolver {
+
+    static private class BPSoftReference extends SoftReference<BeanProperties> {
+        final Class<?> key;
+
+        BPSoftReference(Class<?> key, BeanProperties beanProperties, ReferenceQueue<BeanProperties> refQ) {
+            super(beanProperties, refQ);
+            this.key = key;
+        }
+    }
+
+    static private class SoftConcurrentHashMap extends ConcurrentHashMap<Class<?>, BeanProperties> {
+
+        private static final long serialVersionUID = -178867497897782229L;
+        private static final int CACHE_INIT_SIZE = 1024;
+        private ConcurrentHashMap<Class<?>, BPSoftReference> map = new ConcurrentHashMap<>(CACHE_INIT_SIZE);
+        private ReferenceQueue<BeanProperties> refQ = new ReferenceQueue<>();
+
+        // Remove map entries that have been placed on the queue by GC.
+        private void cleanup() {
+            BPSoftReference BPRef = null;
+            while ((BPRef = (BPSoftReference) refQ.poll()) != null) {
+                map.remove(BPRef.key);
+            }
+        }
+
+        @Override
+        public BeanProperties put(Class<?> key, BeanProperties value) {
+            cleanup();
+            BPSoftReference prev = map.put(key, new BPSoftReference(key, value, refQ));
+            return prev == null ? null : prev.get();
+        }
+
+        @Override
+        public BeanProperties putIfAbsent(Class<?> key, BeanProperties value) {
+            cleanup();
+            BPSoftReference prev = map.putIfAbsent(key, new BPSoftReference(key, value, refQ));
+            return prev == null ? null : prev.get();
+        }
+
+        @Override
+        public BeanProperties get(Object key) {
+            cleanup();
+            BPSoftReference BPRef = map.get(key);
+            if (BPRef == null) {
+                return null;
+            }
+            if (BPRef.get() == null) {
+                // value has been garbage collected, remove entry in map
+                map.remove(key);
+                return null;
+            }
+            return BPRef.get();
+        }
+    }
+
+    private boolean isReadOnly;
+
+    private static final SoftConcurrentHashMap properties = new SoftConcurrentHashMap();
+
+    /*
+     * Defines a property for a bean.
+     */
+    final static class BeanProperty {
+
+        private Method readMethod;
+        private Method writeMethod;
+        private PropertyDescriptor descriptor;
+
+        public BeanProperty(Class<?> baseClass, PropertyDescriptor descriptor) {
+            this.descriptor = descriptor;
+            readMethod = ELUtil.getMethod(baseClass, descriptor.getReadMethod());
+            writeMethod = ELUtil.getMethod(baseClass, descriptor.getWriteMethod());
+        }
+
+        public Class<?> getPropertyType() {
+            return descriptor.getPropertyType();
+        }
+
+        public boolean isReadOnly() {
+            return getWriteMethod() == null;
+        }
+
+        public Method getReadMethod() {
+            return readMethod;
+        }
+
+        public Method getWriteMethod() {
+            return writeMethod;
+        }
+    }
+
+    /*
+     * Defines the properties for a bean.
+     */
+    final static class BeanProperties {
+
+        private final Map<String, BeanProperty> propertyMap = new HashMap<>();
+
+        public BeanProperties(Class<?> baseClass) {
+            PropertyDescriptor[] descriptors;
+            try {
+                BeanInfo info = Introspector.getBeanInfo(baseClass);
+                descriptors = info.getPropertyDescriptors();
+            } catch (IntrospectionException ie) {
+                throw new ELException(ie);
+            }
+
+            for (PropertyDescriptor descriptor : descriptors) {
+                propertyMap.put(descriptor.getName(), new BeanProperty(baseClass, descriptor));
+            }
+        }
+
+        public BeanProperty getBeanProperty(String property) {
+            return propertyMap.get(property);
+        }
+    }
+
+    /**
+     * Creates a new read/write <code>BeanELResolver</code>.
+     */
+    public BeanELResolver() {
+        this.isReadOnly = false;
+    }
+
+    /**
+     * Creates a new <code>BeanELResolver</code> whose read-only status is determined by the given parameter.
+     *
+     * @param isReadOnly <code>true</code> if this resolver cannot modify beans; <code>false</code> otherwise.
+     */
+    public BeanELResolver(boolean isReadOnly) {
+        this.isReadOnly = isReadOnly;
+    }
+
+    /**
+     * If the base object is not <code>null</code>, returns the most general acceptable type that can be set on this bean
+     * property.
+     *
+     * <p>
+     * If the base is not <code>null</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object
+     * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after
+     * this method is called, the caller should ignore the return value.
+     * </p>
+     *
+     * <p>
+     * The provided property will first be coerced to a <code>String</code>. If there is a <code>BeanInfoProperty</code> for
+     * this property and there were no errors retrieving it, the <code>propertyType</code> of the
+     * <code>propertyDescriptor</code> is returned. Otherwise, a <code>PropertyNotFoundException</code> is thrown.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The bean to analyze.
+     * @param property The name of the property to analyze. Will be coerced to a <code>String</code>.
+     * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
+     * the most general acceptable type; otherwise undefined.
+     * @throws NullPointerException if context is <code>null</code>
+     * @throws PropertyNotFoundException if <code>base</code> is not <code>null</code> and the specified property does not
+     * exist or is not readable.
+     * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     */
+    @Override
+    public Class<?> getType(ELContext context, Object base, Object property) {
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base == null || property == null) {
+            return null;
+        }
+
+        BeanProperty beanProperty = getBeanProperty(context, base, property);
+        context.setPropertyResolved(true);
+        return beanProperty.getPropertyType();
+    }
+
+    /**
+     * If the base object is not <code>null</code>, returns the current value of the given property on this bean.
+     *
+     * <p>
+     * If the base is not <code>null</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object
+     * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after
+     * this method is called, the caller should ignore the return value.
+     * </p>
+     *
+     * <p>
+     * The provided property name will first be coerced to a <code>String</code>. If the property is a readable property of
+     * the base object, as per the JavaBeans specification, then return the result of the getter call. If the getter throws
+     * an exception, it is propagated to the caller. If the property is not found or is not readable, a
+     * <code>PropertyNotFoundException</code> is thrown.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The bean on which to get the property.
+     * @param property The name of the property to get. Will be coerced to a <code>String</code>.
+     * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
+     * the value of the given property. Otherwise, undefined.
+     * @throws NullPointerException if context is <code>null</code>.
+     * @throws PropertyNotFoundException if <code>base</code> is not <code>null</code> and the specified property does not
+     * exist or is not readable.
+     * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     */
+    @Override
+    public Object getValue(ELContext context, Object base, Object property) {
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base == null || property == null) {
+            return null;
+        }
+
+        Method method = getBeanProperty(context, base, property).getReadMethod();
+        if (method == null) {
+            throw new PropertyNotFoundException(
+                    getExceptionMessageString(context, "propertyNotReadable", new Object[] { base.getClass().getName(), property.toString() }));
+        }
+
+        Object value;
+        try {
+            value = method.invoke(base, new Object[0]);
+            context.setPropertyResolved(base, property);
+        } catch (ELException ex) {
+            throw ex;
+        } catch (InvocationTargetException ite) {
+            throw new ELException(ite.getCause());
+        } catch (Exception ex) {
+            throw new ELException(ex);
+        }
+
+        return value;
+    }
+
+    /**
+     * If the base object is not <code>null</code>, attempts to set the value of the given property on this bean.
+     *
+     * <p>
+     * If the base is not <code>null</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object
+     * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after
+     * this method is called, the caller can safely assume no value was set.
+     * </p>
+     *
+     * <p>
+     * If this resolver was constructed in read-only mode, this method will always throw
+     * <code>PropertyNotWritableException</code>.
+     * </p>
+     *
+     * <p>
+     * The provided property name will first be coerced to a <code>String</code>. If property is a writable property of
+     * <code>base</code> (as per the JavaBeans Specification), the setter method is called (passing <code>value</code>). If
+     * the property exists but does not have a setter, then a <code>PropertyNotFoundException</code> is thrown. If the
+     * property does not exist, a <code>PropertyNotFoundException</code> is thrown.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The bean on which to set the property.
+     * @param property The name of the property to set. Will be coerced to a <code>String</code>.
+     * @param val The value to be associated with the specified key.
+     * @throws NullPointerException if context is <code>null</code>.
+     * @throws PropertyNotFoundException if <code>base</code> is not <code>null</code> and the specified property does not
+     * exist.
+     * @throws PropertyNotWritableException if this resolver was constructed in read-only mode, or if there is no setter for
+     * the property.
+     * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     */
+    @Override
+    public void setValue(ELContext context, Object base, Object property, Object val) {
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base == null || property == null) {
+            return;
+        }
+
+        if (isReadOnly) {
+            throw new PropertyNotWritableException(getExceptionMessageString(context, "resolverNotwritable", new Object[] { base.getClass().getName() }));
+        }
+
+        Method method = getBeanProperty(context, base, property).getWriteMethod();
+        if (method == null) {
+            throw new PropertyNotWritableException(
+                    getExceptionMessageString(context, "propertyNotWritable", new Object[] { base.getClass().getName(), property.toString() }));
+        }
+
+        try {
+            method.invoke(base, new Object[] { val });
+            context.setPropertyResolved(base, property);
+        } catch (ELException ex) {
+            throw ex;
+        } catch (InvocationTargetException ite) {
+            throw new ELException(ite.getCause());
+        } catch (Exception ex) {
+            if (null == val) {
+                val = "null";
+            }
+            String message = getExceptionMessageString(context, "setPropertyFailed", new Object[] { property.toString(), base.getClass().getName(), val });
+            throw new ELException(message, ex);
+        }
+    }
+
+    /**
+     * If the base object is not <code>null</code>, invoke the method, with the given parameters on this bean. The return
+     * value from the method is returned.
+     *
+     * <p>
+     * If the base is not <code>null</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object
+     * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after
+     * this method is called, the caller should ignore the return value.
+     * </p>
+     *
+     * <p>
+     * The provided method object will first be coerced to a <code>String</code>. The methods in the bean is then examined
+     * and an attempt will be made to select one for invocation. If no suitable can be found, a
+     * <code>MethodNotFoundException</code> is thrown.
+     *
+     * If the given paramTypes is not <code>null</code>, select the method with the given name and parameter types.
+     *
+     * Else select the method with the given name that has the same number of parameters. If there are more than one such
+     * method, the method selection process is undefined.
+     *
+     * Else select the method with the given name that takes a variable number of arguments.
+     *
+     * Note the resolution for overloaded methods will likely be clarified in a future version of the spec.
+     *
+     * The provide parameters are coerced to the corresponding parameter types of the method, and the method is then
+     * invoked.
+     *
+     * @param context The context of this evaluation.
+     * @param base The bean on which to invoke the method
+     * @param methodName The simple name of the method to invoke. Will be coerced to a <code>String</code>. If method is
+     * "&lt;init&gt;"or "&lt;clinit&gt;" a MethodNotFoundException is thrown.
+     * @param paramTypes An array of Class objects identifying the method's formal parameter types, in declared order. Use
+     * an empty array if the method has no parameters. Can be <code>null</code>, in which case the method's formal parameter
+     * types are assumed to be unknown.
+     * @param params The parameters to pass to the method, or <code>null</code> if no parameters.
+     * @return The result of the method invocation (<code>null</code> if the method has a <code>void</code> return type).
+     * @throws MethodNotFoundException if no suitable method can be found.
+     * @throws ELException if an exception was thrown while performing (base, method) resolution. The thrown exception must
+     * be included as the cause property of this exception, if available. If the exception thrown is an
+     * <code>InvocationTargetException</code>, extract its <code>cause</code> and pass it to the <code>ELException</code>
+     * constructor.
+     * @since Jakarta Expression Language 2.2
+     */
+    @Override
+    public Object invoke(ELContext context, Object base, Object methodName, Class<?>[] paramTypes, Object[] params) {
+        if (base == null || methodName == null) {
+            return null;
+        }
+
+        Method method = ELUtil.findMethod(base.getClass(), methodName.toString(), paramTypes, params, false);
+
+        for (Object param : params) {
+            // If the parameters is a LambdaExpression, set the ELContext
+            // for its evaluation
+            if (param instanceof LambdaExpression) {
+                ((LambdaExpression) param).setELContext(context);
+            }
+        }
+
+        Object ret = ELUtil.invokeMethod(context, method, base, params);
+        context.setPropertyResolved(base, methodName);
+        return ret;
+    }
+
+    /**
+     * If the base object is not <code>null</code>, returns whether a call to {@link #setValue} will always fail.
+     *
+     * <p>
+     * If the base is not <code>null</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object
+     * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after
+     * this method is called, the caller can safely assume no value was set.
+     * </p>
+     *
+     * <p>
+     * If this resolver was constructed in read-only mode, this method will always return <code>true</code>.
+     * </p>
+     *
+     * <p>
+     * The provided property name will first be coerced to a <code>String</code>. If property is a writable property of
+     * <code>base</code>, <code>false</code> is returned. If the property is found but is not writable, <code>true</code> is
+     * returned. If the property is not found, a <code>PropertyNotFoundException</code> is thrown.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The bean to analyze.
+     * @param property The name of the property to analyzed. Will be coerced to a <code>String</code>.
+     * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
+     * <code>true</code> if calling the <code>setValue</code> method will always fail or <code>false</code> if it is
+     * possible that such a call may succeed; otherwise undefined.
+     * @throws NullPointerException if context is <code>null</code>
+     * @throws PropertyNotFoundException if <code>base</code> is not <code>null</code> and the specified property does not
+     * exist.
+     * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     */
+    @Override
+    public boolean isReadOnly(ELContext context, Object base, Object property) {
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base == null || property == null) {
+            return false;
+        }
+
+        context.setPropertyResolved(true);
+        if (isReadOnly) {
+            return true;
+        }
+
+        return getBeanProperty(context, base, property).isReadOnly();
+    }
+
+    /**
+     * If the base object is not <code>null</code>, returns an <code>Iterator</code> containing the set of JavaBeans
+     * properties available on the given object. Otherwise, returns <code>null</code>.
+     *
+     * <p>
+     * The <code>Iterator</code> returned must contain zero or more instances of {@link java.beans.FeatureDescriptor}. Each
+     * info object contains information about a property in the bean, as obtained by calling the
+     * <code>BeanInfo.getPropertyDescriptors</code> method. The <code>FeatureDescriptor</code> is initialized using the same
+     * fields as are present in the <code>PropertyDescriptor</code>, with the additional required named attributes
+     * "<code>type</code>" and "<code>resolvableAtDesignTime</code>" set as follows:
+     * <ul>
+     * <li>{@link ELResolver#TYPE} - The runtime type of the property, from
+     * <code>PropertyDescriptor.getPropertyType()</code>.</li>
+     * <li>{@link ELResolver#RESOLVABLE_AT_DESIGN_TIME} - <code>true</code>.</li>
+     * </ul>
+     *
+     *
+     * @param context The context of this evaluation.
+     * @param base The bean to analyze.
+     * @return An <code>Iterator</code> containing zero or more <code>FeatureDescriptor</code> objects, each representing a
+     * property on this bean, or <code>null</code> if the <code>base</code> object is <code>null</code>.
+     */
+    @Override
+    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
+        if (base == null) {
+            return null;
+        }
+
+        BeanInfo info = null;
+        try {
+            info = Introspector.getBeanInfo(base.getClass());
+        } catch (Exception ex) {
+        }
+
+        if (info == null) {
+            return null;
+        }
+
+        ArrayList<FeatureDescriptor> featureDescriptors = new ArrayList<>(info.getPropertyDescriptors().length);
+        for (PropertyDescriptor propertyDescriptor : info.getPropertyDescriptors()) {
+            propertyDescriptor.setValue("type", propertyDescriptor.getPropertyType());
+            propertyDescriptor.setValue("resolvableAtDesignTime", TRUE);
+            featureDescriptors.add(propertyDescriptor);
+        }
+
+        return featureDescriptors.iterator();
+    }
+
+    /**
+     * If the base object is not <code>null</code>, returns the most general type that this resolver accepts for the
+     * <code>property</code> argument. Otherwise, returns <code>null</code>.
+     *
+     * <p>
+     * Assuming the base is not <code>null</code>, this method will always return <code>Object.class</code>. This is because
+     * any object is accepted as a key and is coerced into a string.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The bean to analyze.
+     * @return <code>null</code> if base is <code>null</code>; otherwise <code>Object.class</code>.
+     */
+    @Override
+    public Class<?> getCommonPropertyType(ELContext context, Object base) {
+        if (base == null) {
+            return null;
+        }
+
+        return Object.class;
+    }
+
+    private BeanProperty getBeanProperty(ELContext context, Object base, Object prop) {
+        String property = prop.toString();
+        Class<?> baseClass = base.getClass();
+
+        BeanProperties beanProperties = properties.get(baseClass);
+        if (beanProperties == null) {
+            beanProperties = new BeanProperties(baseClass);
+            properties.put(baseClass, beanProperties);
+        }
+
+        BeanProperty beanProperty = beanProperties.getBeanProperty(property);
+        if (beanProperty == null) {
+            throw new PropertyNotFoundException(getExceptionMessageString(context, "propertyNotFound", new Object[] { baseClass.getName(), property }));
+        }
+
+        return beanProperty;
+    }
+}
diff --git a/api/src/main/java/javax/el/BeanNameELResolver.java b/api/src/main/java/javax/el/BeanNameELResolver.java
new file mode 100644
index 0000000..9c147f1
--- /dev/null
+++ b/api/src/main/java/javax/el/BeanNameELResolver.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package javax.el;
+
+import java.beans.FeatureDescriptor;
+import java.util.Iterator;
+
+/**
+ * <p>
+ * An <code>ELResolver</code> for resolving user or container managed beans.
+ * </p>
+ * <p>
+ * A {@link BeanNameResolver} is required for its proper operation. The following example creates an
+ * <code>ELResolver</code> that resolves the name "bean" to an instance of MyBean.
+ *
+ * <pre>
+ * <code>
+ * ELResovler elr = new BeanNameELResolver(new BeanNameResolver {
+ *    public boolean isNameResolved(String beanName) {
+ *       return "bean".equals(beanName);
+ *    }
+ *    public Object getBean(String beanName) {
+ *       return "bean".equals(beanName)? new MyBean(): null;
+ *    }
+ * });
+ * </code>
+ * </pre>
+ *
+ * @since Jakarta Expression Language 3.0
+ */
+public class BeanNameELResolver extends ELResolver {
+
+    private BeanNameResolver beanNameResolver;
+
+    /**
+     * Constructor
+     *
+     * @param beanNameResolver The {@link BeanNameResolver} that resolves a bean name.
+     */
+    public BeanNameELResolver(BeanNameResolver beanNameResolver) {
+        this.beanNameResolver = beanNameResolver;
+    }
+
+    /**
+     * If the base object is <code>null</code> and the property is a name that is resolvable by the BeanNameResolver,
+     * returns the value resolved by the BeanNameResolver.
+     *
+     * <p>
+     * If name is resolved by the BeanNameResolver, the <code>propertyResolved</code> property of the <code>ELContext</code>
+     * object must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code>
+     * after this method is called, the caller should ignore the return value.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base <code>null</code>
+     * @param property The name of the bean.
+     * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
+     * the value of the bean with the given name. Otherwise, undefined.
+     * @throws NullPointerException if context is <code>null</code>.
+     * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     */
+    @Override
+    public Object getValue(ELContext context, Object base, Object property) {
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base == null && property instanceof String) {
+            if (beanNameResolver.isNameResolved((String) property)) {
+                context.setPropertyResolved(base, property);
+                return beanNameResolver.getBean((String) property);
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * If the base is null and the property is a name that is resolvable by the BeanNameResolver, the bean in the
+     * BeanNameResolver is set to the given value.
+     *
+     * <p>
+     * If the name is resolvable by the BeanNameResolver, or if the BeanNameResolver allows creating a new bean, the
+     * <code>propertyResolved</code> property of the <code>ELContext</code> object must be set to <code>true</code> by the
+     * resolver, before returning. If this property is not <code>true</code> after this method is called, the caller can
+     * safely assume no value has been set.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base <code>null</code>
+     * @param property The name of the bean
+     * @param value The value to set the bean with the given name to.
+     * @throws NullPointerException if context is <code>null</code>
+     * @throws PropertyNotWritableException if the BeanNameResolver does not allow the bean to be modified.
+     * @throws ELException if an exception was thrown while attempting to set the bean with the given name. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     */
+    @Override
+    public void setValue(ELContext context, Object base, Object property, Object value) {
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base == null && property instanceof String) {
+            String beanName = (String) property;
+            if (beanNameResolver.isNameResolved(beanName) || beanNameResolver.canCreateBean(beanName)) {
+                beanNameResolver.setBeanValue(beanName, value);
+                context.setPropertyResolved(base, property);
+            }
+        }
+    }
+
+    /**
+     * If the base is null and the property is a name resolvable by the BeanNameResolver, return the type of the bean.
+     *
+     * <p>
+     * If the name is resolvable by the BeanNameResolver, the <code>propertyResolved</code> property of the
+     * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is
+     * not <code>true</code> after this method is called, the caller can safely assume no value has been set.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base <code>null</code>
+     * @param property The name of the bean.
+     * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
+     * the type of the bean with the given name. Otherwise, undefined.
+     * @throws NullPointerException if context is <code>null</code>.
+     * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     */
+    @Override
+    public Class<?> getType(ELContext context, Object base, Object property) {
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base == null && property instanceof String) {
+            if (beanNameResolver.isNameResolved((String) property)) {
+                context.setPropertyResolved(true);
+                return beanNameResolver.getBean((String) property).getClass();
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * If the base is null and the property is a name resolvable by the BeanNameResolver, attempts to determine if the bean
+     * is writable.
+     *
+     * <p>
+     * If the name is resolvable by the BeanNameResolver, the <code>propertyResolved</code> property of the
+     * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is
+     * not <code>true</code> after this method is called, the caller can safely assume no value has been set.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base <code>null</code>
+     * @param property The name of the bean.
+     * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
+     * <code>true</code> if the property is read-only or <code>false</code> if not; otherwise undefined.
+     * @throws NullPointerException if context is <code>null</code>.
+     * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     */
+    @Override
+    public boolean isReadOnly(ELContext context, Object base, Object property) {
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base == null && property instanceof String) {
+            if (beanNameResolver.isNameResolved((String) property)) {
+                context.setPropertyResolved(true);
+                return beanNameResolver.isReadOnly((String) property);
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Always returns <code>null</code>, since there is no reason to iterate through a list of one element: bean name.
+     *
+     * @param context The context of this evaluation.
+     * @param base <code>null</code>.
+     * @return <code>null</code>.
+     */
+    @Override
+    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
+        return null;
+    }
+
+    /**
+     * Always returns <code>String.class</code>, since a bean name is a String.
+     *
+     * @param context The context of this evaluation.
+     * @param base <code>null</code>.
+     * @return <code>String.class</code>.
+     */
+    @Override
+    public Class<?> getCommonPropertyType(ELContext context, Object base) {
+        return String.class;
+    }
+}
diff --git a/api/src/main/java/javax/el/BeanNameResolver.java b/api/src/main/java/javax/el/BeanNameResolver.java
new file mode 100644
index 0000000..d173174
--- /dev/null
+++ b/api/src/main/java/javax/el/BeanNameResolver.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package javax.el;
+
+/**
+ * Resolves a bean by its known name. This class can be extended to return a bean object given its name, to set a value
+ * to an existing bean, or to create a bean with the value.
+ *
+ * @see BeanNameELResolver
+ *
+ * @since Jakarta Expression Language 3.0
+ */
+public abstract class BeanNameResolver {
+
+    /**
+     * Returns whether the given name is resolved by the BeanNameResolver
+     *
+     * @param beanName The name of the bean.
+     * @return true if the name is resolved by this BeanNameResolver; false otherwise.
+     */
+    public boolean isNameResolved(String beanName) {
+        return false;
+    }
+
+    /**
+     * Returns the bean known by its name.
+     *
+     * @param beanName The name of the bean.
+     * @return The bean with the given name. Can be <code>null</code>.
+     *
+     */
+    public Object getBean(String beanName) {
+        return null;
+    }
+
+    /**
+     * Sets a value to a bean of the given name. If the bean of the given name does not exist and if {@link #canCreateBean}
+     * is <code>true</code>, one is created with the given value.
+     *
+     * @param beanName The name of the bean
+     * @param value The value to set the bean to. Can be <code>null</code>.
+     * @throws PropertyNotWritableException if the bean cannot be modified or created.
+     */
+    public void setBeanValue(String beanName, Object value) throws PropertyNotWritableException {
+        throw new PropertyNotWritableException();
+    }
+
+    /**
+     * Indicates if the bean of the given name is read-only or writable
+     *
+     * @param beanName The name of the bean
+     * @return <code>true</code> if the bean can be set to a new value. <code>false</code> otherwise.
+     */
+    public boolean isReadOnly(String beanName) {
+        return true;
+    }
+
+    /**
+     * Allow creating a bean of the given name if it does not exist.
+     *
+     * @param beanName The name of the bean
+     * @return <code>true</code> if bean creation is supported <code>false</code> otherwise.
+     */
+    public boolean canCreateBean(String beanName) {
+        return false;
+    }
+}
diff --git a/api/src/main/java/javax/el/CompositeELResolver.java b/api/src/main/java/javax/el/CompositeELResolver.java
new file mode 100644
index 0000000..1e0e9e1
--- /dev/null
+++ b/api/src/main/java/javax/el/CompositeELResolver.java
@@ -0,0 +1,539 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package javax.el;
+
+import java.beans.FeatureDescriptor;
+import java.util.Iterator;
+
+/**
+ * Maintains an ordered composite list of child <code>ELResolver</code>s.
+ *
+ * <p>
+ * Though only a single <code>ELResolver</code> is associated with an <code>ELContext</code>, there are usually multiple
+ * resolvers considered for any given variable or property resolution. <code>ELResolver</code>s are combined together
+ * using a <code>CompositeELResolver</code>, to define rich semantics for evaluating an expression.
+ * </p>
+ *
+ * <p>
+ * For the {@link #getValue}, {@link #getType}, {@link #setValue} and {@link #isReadOnly} methods, an
+ * <code>ELResolver</code> is not responsible for resolving all possible (base, property) pairs. In fact, most resolvers
+ * will only handle a <code>base</code> of a single type. To indicate that a resolver has successfully resolved a
+ * particular (base, property) pair, it must set the <code>propertyResolved</code> property of the
+ * <code>ELContext</code> to <code>true</code>. If it could not handle the given pair, it must leave this property
+ * alone. The caller must ignore the return value of the method if <code>propertyResolved</code> is <code>false</code>.
+ * </p>
+ *
+ * <p>
+ * The <code>CompositeELResolver</code> initializes the <code>ELContext.propertyResolved</code> flag to
+ * <code>false</code>, and uses it as a stop condition for iterating through its component resolvers.
+ * </p>
+ *
+ * <p>
+ * The <code>ELContext.propertyResolved</code> flag is not used for the design-time methods
+ * {@link #getFeatureDescriptors} and {@link #getCommonPropertyType}. Instead, results are collected and combined from
+ * all child <code>ELResolver</code>s for these methods.
+ * </p>
+ *
+ * @see ELContext
+ * @see ELResolver
+ * @since Jakarta Server Pages 2.1
+ */
+public class CompositeELResolver extends ELResolver {
+
+    public CompositeELResolver() {
+        this.size = 0;
+        this.elResolvers = new ELResolver[16];
+    }
+
+    /**
+     * Adds the given resolver to the list of component resolvers.
+     *
+     * <p>
+     * Resolvers are consulted in the order in which they are added.
+     * </p>
+     *
+     * @param elResolver The component resolver to add.
+     * @throws NullPointerException If the provided resolver is <code>null</code>.
+     */
+    public void add(ELResolver elResolver) {
+        if (elResolver == null) {
+            throw new NullPointerException();
+        }
+
+        if (size >= elResolvers.length) {
+            ELResolver[] newResolvers = new ELResolver[size * 2];
+            System.arraycopy(elResolvers, 0, newResolvers, 0, size);
+            elResolvers = newResolvers;
+        }
+
+        elResolvers[size++] = elResolver;
+    }
+
+    /**
+     * Attempts to resolve the given <code>property</code> object on the given <code>base</code> object by querying all
+     * component resolvers.
+     *
+     * <p>
+     * If this resolver handles the given (base, property) pair, the <code>propertyResolved</code> property of the
+     * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is
+     * not <code>true</code> after this method is called, the caller should ignore the return value.
+     *
+     * <p>
+     * First, <code>propertyResolved</code> is set to <code>false</code> on the provided <code>ELContext</code>.
+     *
+     * <p>
+     * Next, for each component resolver in this composite:
+     * <ol>
+     * <li>The <code>getValue()</code> method is called, passing in the provided <code>context</code>, <code>base</code> and
+     * <code>property</code>.</li>
+     * <li>If the <code>ELContext</code>'s <code>propertyResolved</code> flag is <code>false</code> then iteration
+     * continues.</li>
+     * <li>Otherwise, iteration stops and no more component resolvers are considered. The value returned by
+     * <code>getValue()</code> is returned by this method.</li>
+     * </ol>
+     *
+     * <p>
+     * If none of the component resolvers were able to perform this operation, the value <code>null</code> is returned and
+     * the <code>propertyResolved</code> flag remains set to <code>false</code>.
+     *
+     * <p>
+     * Any exception thrown by component resolvers during the iteration is propagated to the caller of this method.
+     *
+     * @param context The context of this evaluation.
+     * @param base The base object whose property value is to be returned, or <code>null</code> to resolve a top-level
+     * variable.
+     * @param property The property or variable to be resolved.
+     * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
+     * the result of the variable or property resolution; otherwise undefined.
+     * @throws NullPointerException if context is <code>null</code>
+     * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but
+     * the specified variable or property does not exist or is not readable.
+     * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     */
+    @Override
+    public Object getValue(ELContext context, Object base, Object property) {
+        context.setPropertyResolved(false);
+
+        Object value = null;
+        for (int i = 0; i < size; i++) {
+            value = elResolvers[i].getValue(context, base, property);
+            if (context.isPropertyResolved()) {
+                return value;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Attempts to resolve and invoke the given <code>method</code> on the given <code>base</code> object by querying all
+     * component resolvers.
+     *
+     * <p>
+     * If this resolver handles the given (base, method) pair, the <code>propertyResolved</code> property of the
+     * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is
+     * not <code>true</code> after this method is called, the caller should ignore the return value.
+     * </p>
+     *
+     * <p>
+     * First, <code>propertyResolved</code> is set to <code>false</code> on the provided <code>ELContext</code>.
+     * </p>
+     *
+     * <p>
+     * Next, for each component resolver in this composite:
+     * <ol>
+     * <li>The <code>invoke()</code> method is called, passing in the provided <code>context</code>, <code>base</code>,
+     * <code>method</code>, <code>paramTypes</code>, and <code>params</code>.</li>
+     * <li>If the <code>ELContext</code>'s <code>propertyResolved</code> flag is <code>false</code> then iteration
+     * continues.</li>
+     * <li>Otherwise, iteration stops and no more component resolvers are considered. The value returned by
+     * <code>getValue()</code> is returned by this method.</li>
+     * </ol>
+     *
+     * <p>
+     * If none of the component resolvers were able to perform this operation, the value <code>null</code> is returned and
+     * the <code>propertyResolved</code> flag remains set to <code>false</code>
+     * </p>
+     * .
+     *
+     * <p>
+     * Any exception thrown by component resolvers during the iteration is propagated to the caller of this method.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The bean on which to invoke the method
+     * @param method The simple name of the method to invoke. Will be coerced to a <code>String</code>.
+     * @param paramTypes An array of Class objects identifying the method's formal parameter types, in declared order. Use
+     * an empty array if the method has no parameters. Can be <code>null</code>, in which case the method's formal parameter
+     * types are assumed to be unknown.
+     * @param params The parameters to pass to the method, or <code>null</code> if no parameters.
+     * 
+     * @return The result of the method invocation (<code>null</code> if the method has a <code>void</code> return type).
+     * 
+     * @since Jakarta Expression Language 2.2
+     */
+    @Override
+    public Object invoke(ELContext context, Object base, Object method, Class<?>[] paramTypes, Object[] params) {
+        context.setPropertyResolved(false);
+
+        Object value;
+        for (int i = 0; i < size; i++) {
+            value = elResolvers[i].invoke(context, base, method, paramTypes, params);
+            if (context.isPropertyResolved()) {
+                return value;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * For a given <code>base</code> and <code>property</code>, attempts to identify the most general type that is
+     * acceptable for an object to be passed as the <code>value</code> parameter in a future call to the {@link #setValue}
+     * method. The result is obtained by querying all component resolvers.
+     *
+     * <p>
+     * If this resolver handles the given (base, property) pair, the <code>propertyResolved</code> property of the
+     * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is
+     * not <code>true</code> after this method is called, the caller should ignore the return value.
+     * </p>
+     *
+     * <p>
+     * First, <code>propertyResolved</code> is set to <code>false</code> on the provided <code>ELContext</code>.
+     * </p>
+     *
+     * <p>
+     * Next, for each component resolver in this composite:
+     * <ol>
+     * <li>The <code>getType()</code> method is called, passing in the provided <code>context</code>, <code>base</code> and
+     * <code>property</code>.</li>
+     * <li>If the <code>ELContext</code>'s <code>propertyResolved</code> flag is <code>false</code> then iteration
+     * continues.</li>
+     * <li>Otherwise, iteration stops and no more component resolvers are considered. The value returned by
+     * <code>getType()</code> is returned by this method.</li>
+     * </ol>
+     *
+     * <p>
+     * If none of the component resolvers were able to perform this operation, the value <code>null</code> is returned and
+     * the <code>propertyResolved</code> flag remains set to <code>false</code>.
+     * </p>
+     *
+     *
+     * <p>
+     * Any exception thrown by component resolvers during the iteration is propagated to the caller of this method.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The base object whose property value is to be analyzed, or <code>null</code> to analyze a top-level
+     * variable.
+     * @param property The property or variable to return the acceptable type for.
+     * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
+     * the most general acceptable type; otherwise undefined.
+     * @throws NullPointerException if context is <code>null</code>
+     * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but
+     * the specified variable or property does not exist or is not readable.
+     * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     */
+    @Override
+    public Class<?> getType(ELContext context, Object base, Object property) {
+        context.setPropertyResolved(false);
+
+        Class<?> type;
+        for (int i = 0; i < size; i++) {
+            type = elResolvers[i].getType(context, base, property);
+            if (context.isPropertyResolved()) {
+                return type;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Attempts to set the value of the given <code>property</code> object on the given <code>base</code> object. All
+     * component resolvers are asked to attempt to set the value.
+     *
+     * <p>
+     * If this resolver handles the given (base, property) pair, the <code>propertyResolved</code> property of the
+     * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is
+     * not <code>true</code> after this method is called, the caller can safely assume no value has been set.
+     *
+     * <p>
+     * First, <code>propertyResolved</code> is set to <code>false</code> on the provided <code>ELContext</code>.
+     *
+     * <p>
+     * Next, for each component resolver in this composite:
+     * <ol>
+     * <li>The <code>setValue()</code> method is called, passing in the provided <code>context</code>, <code>base</code>,
+     * <code>property</code> and <code>value</code>.</li>
+     * <li>If the <code>ELContext</code>'s <code>propertyResolved</code> flag is <code>false</code> then iteration
+     * continues.</li>
+     * <li>Otherwise, iteration stops and no more component resolvers are considered.</li>
+     * </ol>
+     *
+     * <p>
+     * If none of the component resolvers were able to perform this operation, the <code>propertyResolved</code> flag
+     * remains set to <code>false</code>.
+     *
+     * <p>
+     * Any exception thrown by component resolvers during the iteration is propagated to the caller of this method.
+     *
+     * @param context The context of this evaluation.
+     * @param base The base object whose property value is to be set, or <code>null</code> to set a top-level variable.
+     * @param property The property or variable to be set.
+     * @param val The value to set the property or variable to.
+     * @throws NullPointerException if context is <code>null</code>
+     * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but
+     * the specified variable or property does not exist.
+     * @throws PropertyNotWritableException if the given (base, property) pair is handled by this <code>ELResolver</code>
+     * but the specified variable or property is not writable.
+     * @throws ELException if an exception was thrown while attempting to set the property or variable. The thrown exception
+     * must be included as the cause property of this exception, if available.
+     */
+    @Override
+    public void setValue(ELContext context, Object base, Object property, Object val) {
+        context.setPropertyResolved(false);
+
+        for (int i = 0; i < size; i++) {
+            elResolvers[i].setValue(context, base, property, val);
+            if (context.isPropertyResolved()) {
+                return;
+            }
+        }
+    }
+
+    /**
+     * For a given <code>base</code> and <code>property</code>, attempts to determine whether a call to {@link #setValue}
+     * will always fail. The result is obtained by querying all component resolvers.
+     *
+     * <p>
+     * If this resolver handles the given (base, property) pair, the <code>propertyResolved</code> property of the
+     * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is
+     * not <code>true</code> after this method is called, the caller should ignore the return value.
+     * </p>
+     *
+     * <p>
+     * First, <code>propertyResolved</code> is set to <code>false</code> on the provided <code>ELContext</code>.
+     * </p>
+     *
+     * <p>
+     * Next, for each component resolver in this composite:
+     * <ol>
+     * <li>The <code>isReadOnly()</code> method is called, passing in the provided <code>context</code>, <code>base</code>
+     * and <code>property</code>.</li>
+     * <li>If the <code>ELContext</code>'s <code>propertyResolved</code> flag is <code>false</code> then iteration
+     * continues.</li>
+     * <li>Otherwise, iteration stops and no more component resolvers are considered. The value returned by
+     * <code>isReadOnly()</code> is returned by this method.</li>
+     * </ol>
+     *
+     * <p>
+     * If none of the component resolvers were able to perform this operation, the value <code>false</code> is returned and
+     * the <code>propertyResolved</code> flag remains set to <code>false</code>
+     * </p>
+     * .
+     *
+     * <p>
+     * Any exception thrown by component resolvers during the iteration is propagated to the caller of this method.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The base object whose property value is to be analyzed, or <code>null</code> to analyze a top-level
+     * variable.
+     * @param property The property or variable to return the read-only status for.
+     * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
+     * <code>true</code> if the property is read-only or <code>false</code> if not; otherwise undefined.
+     * @throws NullPointerException if context is <code>null</code>
+     * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but
+     * the specified variable or property does not exist.
+     * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     */
+    @Override
+    public boolean isReadOnly(ELContext context, Object base, Object property) {
+        context.setPropertyResolved(false);
+
+        boolean readOnly;
+        for (int i = 0; i < size; i++) {
+            readOnly = elResolvers[i].isReadOnly(context, base, property);
+            if (context.isPropertyResolved()) {
+                return readOnly;
+            }
+        }
+
+        return false; // Does not matter
+    }
+
+    /**
+     * Returns information about the set of variables or properties that can be resolved for the given <code>base</code>
+     * object. One use for this method is to assist tools in auto-completion. The results are collected from all component
+     * resolvers.
+     *
+     * <p>
+     * The <code>propertyResolved</code> property of the <code>ELContext</code> is not relevant to this method. The results
+     * of all <code>ELResolver</code>s are concatenated.
+     * </p>
+     *
+     * <p>
+     * The <code>Iterator</code> returned is an iterator over the collection of <code>FeatureDescriptor</code> objects
+     * returned by the iterators returned by each component resolver's <code>getFeatureDescriptors</code> method. If
+     * <code>null</code> is returned by a resolver, it is skipped.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The base object whose set of valid properties is to be enumerated, or <code>null</code> to enumerate the
+     * set of top-level variables that this resolver can evaluate.
+     * @return An <code>Iterator</code> containing zero or more (possibly infinitely more) <code>FeatureDescriptor</code>
+     * objects, or <code>null</code> if this resolver does not handle the given <code>base</code> object or that the results
+     * are too complex to represent with this method
+     */
+    @Override
+    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
+        return new CompositeIterator(elResolvers, size, context, base);
+    }
+
+    /**
+     * Returns the most general type that this resolver accepts for the <code>property</code> argument, given a
+     * <code>base</code> object. One use for this method is to assist tools in auto-completion. The result is obtained by
+     * querying all component resolvers.
+     *
+     * <p>
+     * The <code>Class</code> returned is the most specific class that is a common superclass of all the classes returned by
+     * each component resolver's <code>getCommonPropertyType</code> method. If <code>null</code> is returned by a resolver,
+     * it is skipped.
+     *
+     * @param context The context of this evaluation.
+     * @param base The base object to return the most general property type for, or <code>null</code> to enumerate the set
+     * of top-level variables that this resolver can evaluate.
+     * 
+     * @return <code>null</code> if this <code>ELResolver</code> does not know how to handle the given <code>base</code>
+     * object; otherwise <code>Object.class</code> if any type of <code>property</code> is accepted; otherwise the most
+     * general <code>property</code> type accepted for the given <code>base</code>.
+     */
+    @Override
+    public Class<?> getCommonPropertyType(ELContext context, Object base) {
+        Class<?> commonPropertyType = null;
+        for (int i = 0; i < size; i++) {
+
+            Class<?> type = elResolvers[i].getCommonPropertyType(context, base);
+            if (type == null) {
+                // skip this Jakarta Expression Language Resolver
+                continue;
+            } else if (commonPropertyType == null) {
+                commonPropertyType = type;
+            } else if (commonPropertyType.isAssignableFrom(type)) {
+                continue;
+            } else if (type.isAssignableFrom(commonPropertyType)) {
+                commonPropertyType = type;
+            } else {
+                // Don't have a commonPropertyType
+                return null;
+            }
+        }
+
+        return commonPropertyType;
+    }
+
+    /**
+     * Converts an object to a specific type.
+     *
+     * <p>
+     * An <code>ELException</code> is thrown if an error occurs during the conversion.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param obj The object to convert.
+     * @param targetType The target type for the convertion.
+     * 
+     * @throws ELException thrown if errors occur.
+     *
+     * @since Jakarta Expression Language 3.0
+     */
+    @Override
+    public Object convertToType(ELContext context, Object obj, Class<?> targetType) {
+        context.setPropertyResolved(false);
+
+        Object value = null;
+        for (int i = 0; i < size; i++) {
+            value = elResolvers[i].convertToType(context, obj, targetType);
+            if (context.isPropertyResolved()) {
+                return value;
+            }
+        }
+
+        return null;
+    }
+
+    private ELResolver[] elResolvers;
+    private int size;
+
+    private static class CompositeIterator implements Iterator<FeatureDescriptor> {
+
+        ELResolver[] resolvers;
+        int size;
+        int index = 0;
+        Iterator<FeatureDescriptor> propertyIter = null;
+        ELContext context;
+        Object base;
+
+        CompositeIterator(ELResolver[] resolvers, int size, ELContext context, Object base) {
+            this.resolvers = resolvers;
+            this.size = size;
+            this.context = context;
+            this.base = base;
+        }
+
+        @Override
+        public boolean hasNext() {
+            if (propertyIter == null || !propertyIter.hasNext()) {
+                while (index < size) {
+                    ELResolver elResolver = resolvers[index++];
+                    propertyIter = elResolver.getFeatureDescriptors(context, base);
+                    if (propertyIter != null) {
+                        return propertyIter.hasNext();
+                    }
+                }
+                return false;
+            }
+            return propertyIter.hasNext();
+        }
+
+        @Override
+        public FeatureDescriptor next() {
+            if (propertyIter == null || !propertyIter.hasNext()) {
+                while (index < size) {
+                    ELResolver elResolver = resolvers[index++];
+                    propertyIter = elResolver.getFeatureDescriptors(context, base);
+                    if (propertyIter != null) {
+                        return propertyIter.next();
+                    }
+                }
+                return null;
+            }
+            return propertyIter.next();
+        }
+
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
diff --git a/api/src/main/java/javax/el/ELClass.java b/api/src/main/java/javax/el/ELClass.java
new file mode 100644
index 0000000..86355ed
--- /dev/null
+++ b/api/src/main/java/javax/el/ELClass.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package javax.el;
+
+/**
+ * A runtime representation of a Class in the Jakarta Expression Language expressions. It encapsulates the
+ * java.lang.Class instance.
+ *
+ * <p>
+ * This class is used only in {@link StaticFieldELResolver} and will probably only be of interest to Jakarta Expression
+ * Language implementors, and not Jakarta Expression Language users.
+ *
+ * @since Jakarta Expression Language 3.0
+ */
+public class ELClass {
+
+    private Class<?> klass;
+
+    /**
+     * Constructor
+     *
+     * @param klass The Class instance
+     */
+    public ELClass(Class<?> klass) {
+        this.klass = klass;
+    }
+
+    /**
+     * Returns the Class instance
+     *
+     * @return The Class instance
+     */
+    public Class<?> getKlass() {
+        return this.klass;
+    }
+}
diff --git a/api/src/main/java/javax/el/ELContext.java b/api/src/main/java/javax/el/ELContext.java
new file mode 100644
index 0000000..0b95e59
--- /dev/null
+++ b/api/src/main/java/javax/el/ELContext.java
@@ -0,0 +1,448 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package javax.el;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Stack;
+
+/**
+ * Context information for expression parsing and evaluation.
+ *
+ * <p>
+ * To parse or evaluate an {@link Expression}, an <code>ELContext</code> must be provided. The <code>ELContext</code>
+ * holds:
+ * <ul>
+ * <li>a reference to {@link FunctionMapper} that will be used to resolve Jakarta Expression Language Functions. This is
+ * used only in parsing.</li>
+ * <li>a reference to {@link VariableMapper} that will be used to resolve Jakarta Expression Language Variables. This is
+ * used only in parsing.</li>
+ * <li>a reference to the base {@link ELResolver} that will be consulted to resolve model objects and their
+ * properties</li>
+ * <li>a collection of all the relevant context objects for use by <code>ELResolver</code>s</li>
+ * <li>state information during the evaluation of an expression, such as whether a property has been resolved yet</li>
+ * <li>a reference to {@link ImportHandler} that will be consulted to resolve classes that have been imported</li>
+ * <li>a reference to the arguments for the active {@link LambdaExpression}s</li>
+ * <li>a reference to the list of registered evaluation listeners</li>
+ * </ul>
+ *
+ * <p>
+ * The collection of context objects is necessary because each <code>ELResolver</code> may need access to a different
+ * context object. For example, Jakarta Server Pages and Jakarta Faces resolvers need access to a
+ * <code>javax.servlet.jsp.JspContext</code> and a <code>javax.faces.context.FacesContext</code>, respectively.
+ *
+ * <p>
+ * When used in a web container, the creation of <code>ELContext</code> objects is controlled through the underlying
+ * technology. For example, in Jakarta Server Pages the <code>JspContext.getELContext()</code> factory method is used.
+ * Some technologies provide the ability to add an {@link ELContextListener} so that applications and frameworks can
+ * ensure their own context objects are attached to any newly created <code>ELContext</code>.
+ *
+ * <p>
+ * When used in a stand-alone environment, {@link StandardELContext} provides a default <code>ELContext</code>, which is
+ * managed and modified by {@link ELManager}.
+ *
+ * <p>
+ * Because it stores state during expression evaluation, an <code>ELContext</code> object is not thread-safe. Care
+ * should be taken to never share an <code>ELContext</code> instance between two or more threads.
+ *
+ * @see ELContextListener
+ * @see ELContextEvent
+ * @see ELResolver
+ * @see FunctionMapper
+ * @see VariableMapper
+ * @see ImportHandler
+ * @see LambdaExpression
+ * @see StandardELContext
+ * 
+ * @since Jakarta Expression Language 2.1 and Jakarta Expression Language 3.0
+ */
+public abstract class ELContext {
+
+    private boolean resolved;
+    private HashMap<Class<?>, Object> map = new HashMap<>();
+    private transient List<EvaluationListener> listeners;
+    private Stack<Map<String, Object>> lambdaArgs;
+    private ImportHandler importHandler;
+    private Locale locale;
+
+    /**
+     * Called to indicate that a <code>ELResolver</code> has successfully resolved a given (base, property) pair. Use
+     * {@link #setPropertyResolved(Object, Object)} if resolved is true and to notify {@link EvaluationListener}s.
+     *
+     * <p>
+     * The {@link CompositeELResolver} checks this property to determine whether it should consider or skip other component
+     * resolvers.
+     * </p>
+     *
+     * @see CompositeELResolver
+     * @param resolved true if the property has been resolved, or false if not.
+     */
+    public void setPropertyResolved(boolean resolved) {
+        this.resolved = resolved;
+    }
+
+    /**
+     * Called to indicate that a <code>ELResolver</code> has successfully resolved a given (base, property) pair and to
+     * notify the {@link EvaluationListener}s.
+     *
+     * <p>
+     * The {@link CompositeELResolver} checks this property to determine whether it should consider or skip other component
+     * resolvers.
+     *
+     * @see CompositeELResolver
+     * @param base The base object
+     * @param property The property object
+     *
+     * @since Jakarta Expression Language 3.0
+     */
+    public void setPropertyResolved(Object base, Object property) {
+        setPropertyResolved(true); // Don't set the variable here, for 2.2 users
+                                   // ELContext may be overridden or delegated.
+        notifyPropertyResolved(base, property);
+    }
+
+    /**
+     * Returns whether an {@link ELResolver} has successfully resolved a given (base, property) pair.
+     *
+     * <p>
+     * The {@link CompositeELResolver} checks this property to determine whether it should consider or skip other component
+     * resolvers.
+     *
+     * @see CompositeELResolver
+     * @return true if the property has been resolved, or false if not.
+     */
+    public boolean isPropertyResolved() {
+        return resolved;
+    }
+
+    /**
+     * Associates a context object with this <code>ELContext</code>.
+     *
+     * <p>
+     * The <code>ELContext</code> maintains a collection of context objects relevant to the evaluation of an expression.
+     * These context objects are used by <code>ELResolver</code>s. This method is used to add a context object to that
+     * collection.
+     * </p>
+     *
+     * <p>
+     * By convention, the <code>contextObject</code> will be of the type specified by the <code>key</code>. However, this is
+     * not required and the key is used strictly as a unique identifier.
+     * </p>
+     *
+     * @param key The key used by an @{link ELResolver} to identify this context object.
+     * @param contextObject The context object to add to the collection.
+     * @throws NullPointerException if key is null or contextObject is null.
+     */
+    public void putContext(Class key, Object contextObject) {
+        if (key == null || contextObject == null) {
+            throw new NullPointerException();
+        }
+
+        map.put(key, contextObject);
+    }
+
+    /**
+     * Returns the context object associated with the given key.
+     *
+     * <p>
+     * The <code>ELContext</code> maintains a collection of context objects relevant to the evaluation of an expression.
+     * These context objects are used by <code>ELResolver</code>s. This method is used to retrieve the context with the
+     * given key from the collection.
+     * </p>
+     *
+     * <p>
+     * By convention, the object returned will be of the type specified by the <code>key</code>. However, this is not
+     * required and the key is used strictly as a unique identifier.
+     * </p>
+     *
+     * @param key The unique identifier that was used to associate the context object with this <code>ELContext</code>.
+     * @return The context object associated with the given key, or null if no such context was found.
+     * @throws NullPointerException if key is null.
+     */
+    public Object getContext(Class key) {
+        if (key == null) {
+            throw new NullPointerException();
+        }
+
+        return map.get(key);
+    }
+
+    /**
+     * Retrieves the <code>ELResolver</code> associated with this context.
+     *
+     * <p>
+     * The <code>ELContext</code> maintains a reference to the <code>ELResolver</code> that will be consulted to resolve
+     * variables and properties during an expression evaluation. This method retrieves the reference to the resolver.
+     * </p>
+     *
+     * <p>
+     * Once an <code>ELContext</code> is constructed, the reference to the <code>ELResolver</code> associated with the
+     * context cannot be changed.
+     * </p>
+     *
+     * @return The resolver to be consulted for variable and property resolution during expression evaluation.
+     */
+    public abstract ELResolver getELResolver();
+
+    /**
+     * Retrieves the <code>ImportHandler</code> associated with this <code>ELContext</code>.
+     *
+     * @return The import handler to manage imports of classes and packages.
+     * 
+     * @since Jakarta Expression Language 3.0
+     */
+    public ImportHandler getImportHandler() {
+        if (importHandler == null) {
+            importHandler = new ImportHandler();
+        }
+
+        return importHandler;
+    }
+
+    /**
+     * Retrieves the <code>FunctionMapper</code> associated with this <code>ELContext</code>.
+     *
+     * @return The function mapper to be consulted for the resolution of Jakarta Expression Language functions.
+     */
+    public abstract FunctionMapper getFunctionMapper();
+
+    /**
+     * Get the <code>Locale</code> stored by a previous invocation to {@link #setLocale}. If this method returns non
+     * <code>null</code>, this <code>Locale</code> must be used for all localization needs in the implementation. The
+     * <code>Locale</code> must not be cached to allow for applications that change <code>Locale</code> dynamically.
+     *
+     * @return The <code>Locale</code> in which this instance is operating. Used primarily for message localization.
+     */
+    public Locale getLocale() {
+        return locale;
+    }
+
+    /**
+     * Sets the <code>Locale</code> for this instance.
+     *
+     * <p>
+     * This method may be called by the party creating the instance, such as Jakarta Faces or Jakarta Server Pages, to
+     * enable the Jakarta Expression Language implementation to provide localized messages to the user. If no
+     * <code>Locale</code> is set, the implementation must use the locale returned by <code>Locale.getDefault( )</code>.
+     *
+     * @param locale the locale for this instance
+     */
+    public void setLocale(Locale locale) {
+        this.locale = locale;
+    }
+
+    /**
+     * Retrieves the <code>VariableMapper</code> associated with this <code>ELContext</code>.
+     *
+     * @return The variable mapper to be consulted for the resolution of Jakarta Expression Language variables.
+     */
+    public abstract VariableMapper getVariableMapper();
+
+    /**
+     * Registers an evaluation listener to the ELContext.
+     *
+     * @param listener The listener to be added.
+     *
+     * @since Jakarta Expression Language 3.0
+     */
+    public void addEvaluationListener(EvaluationListener listener) {
+        if (listeners == null) {
+            listeners = new ArrayList<>();
+        }
+
+        listeners.add(listener);
+    }
+
+    /**
+     * Returns the list of registered evaluation listeners.
+     *
+     * @return The list of registered evaluation listeners.
+     *
+     * @since Jakarta Expression Language 3.0
+     */
+    public List<EvaluationListener> getEvaluationListeners() {
+        return listeners;
+    }
+
+    /**
+     * Notifies the listeners before an Jakarta Expression Language expression is evaluated
+     *
+     * @param expr The Jakarta Expression Language expression string to be evaluated
+     */
+    public void notifyBeforeEvaluation(String expr) {
+        if (getEvaluationListeners() == null) {
+            return;
+        }
+
+        for (EvaluationListener listener : getEvaluationListeners()) {
+            listener.beforeEvaluation(this, expr);
+        }
+    }
+
+    /**
+     * Notifies the listeners after an Jakarta Expression Language expression is evaluated
+     *
+     * @param expr The Jakarta Expression Language expression string that has been evaluated
+     */
+    public void notifyAfterEvaluation(String expr) {
+        if (getEvaluationListeners() == null) {
+            return;
+        }
+
+        for (EvaluationListener listener : getEvaluationListeners()) {
+            listener.afterEvaluation(this, expr);
+        }
+    }
+
+    /**
+     * Notifies the listeners when the (base, property) pair is resolved
+     *
+     * @param base The base object
+     * @param property The property Object
+     */
+    public void notifyPropertyResolved(Object base, Object property) {
+        if (getEvaluationListeners() == null) {
+            return;
+        }
+
+        for (EvaluationListener listener : getEvaluationListeners()) {
+            listener.propertyResolved(this, base, property);
+        }
+    }
+
+    /**
+     * Inquires if the name is a LambdaArgument
+     *
+     * @param arg A possible Lambda formal parameter name
+     * @return true if arg is a LambdaArgument, false otherwise.
+     */
+    public boolean isLambdaArgument(String arg) {
+        if (lambdaArgs == null) {
+            return false;
+        }
+
+        for (int i = lambdaArgs.size() - 1; i >= 0; i--) {
+            Map<String, Object> lmap = lambdaArgs.elementAt(i);
+            if (lmap.containsKey(arg)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Retrieves the Lambda argument associated with a formal parameter. If the Lambda expression is nested within other
+     * Lambda expressions, the arguments for the current Lambda expression is first searched, and if not found, the
+     * arguments for the immediate nesting Lambda expression then searched, and so on.
+     *
+     * @param arg The formal parameter for the Lambda argument
+     * @return The object associated with formal parameter. Null if no object has been associated with the parameter.
+     * 
+     * @since Jakarta Expression Language 3.0
+     */
+    public Object getLambdaArgument(String arg) {
+        if (lambdaArgs == null) {
+            return null;
+        }
+
+        for (int i = lambdaArgs.size() - 1; i >= 0; i--) {
+            Map<String, Object> lmap = lambdaArgs.elementAt(i);
+            Object v = lmap.get(arg);
+            if (v != null) {
+                return v;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Installs a Lambda argument map, in preparation for the evaluation of a Lambda expression. The arguments in the map
+     * will be in scope during the evaluation of the Lambda expression.
+     *
+     * @param args The Lambda arguments map
+     * 
+     * @since Jakarta Expression Language 3.0
+     */
+    public void enterLambdaScope(Map<String, Object> args) {
+        if (lambdaArgs == null) {
+            lambdaArgs = new Stack<>();
+        }
+
+        lambdaArgs.push(args);
+    }
+
+    /**
+     * Exits the Lambda expression evaluation. The Lambda argument map that was previously installed is removed.
+     *
+     * @since Jakarta Expression Language 3.0
+     */
+    public void exitLambdaScope() {
+        if (lambdaArgs != null) {
+            lambdaArgs.pop();
+        }
+    }
+
+    /**
+     * Converts an object to a specific type. If a custom converter in the <code>ELResolver</code> handles this conversion,
+     * it is used. Otherwise the standard coercions is applied.
+     *
+     * <p>
+     * An <code>ELException</code> is thrown if an error occurs during the conversion.
+     *
+     * @param obj The object to convert.
+     * @param targetType The target type for the conversion.
+     * 
+     * @return object converted to <code>targetType</code>
+     * @throws ELException thrown if errors occur.
+     *
+     * @since Jakarta Expression Language 3.0
+     */
+    public Object convertToType(Object obj, Class<?> targetType) {
+        boolean propertyResolvedSave = isPropertyResolved();
+        try {
+            setPropertyResolved(false);
+            ELResolver elResolver = getELResolver();
+            if (elResolver != null) {
+                Object res = elResolver.convertToType(this, obj, targetType);
+                if (isPropertyResolved()) {
+                    return res;
+                }
+            }
+        } catch (ELException ex) {
+            throw ex;
+        } catch (Exception ex) {
+            throw new ELException(ex);
+        } finally {
+            setPropertyResolved(propertyResolvedSave);
+        }
+
+        ExpressionFactory exprFactory = (ExpressionFactory) getContext(ExpressionFactory.class);
+        if (exprFactory == null) {
+            exprFactory = ELUtil.getExpressionFactory();
+        }
+
+        return exprFactory.coerceToType(obj, targetType);
+    }
+
+}
diff --git a/api/src/main/java/javax/el/ELContextEvent.java b/api/src/main/java/javax/el/ELContextEvent.java
new file mode 100644
index 0000000..e59cc16
--- /dev/null
+++ b/api/src/main/java/javax/el/ELContextEvent.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package javax.el;
+
+import java.util.EventObject;
+
+/**
+ * An event which indicates that an {@link ELContext} has been created. The source object is the ELContext that was
+ * created.
+ *
+ * @see ELContext
+ * @see ELContextListener
+ * 
+ * @since Jakarta Server Pages 2.1
+ */
+public class ELContextEvent extends EventObject {
+
+    private static final long serialVersionUID = 1255131906285426769L;
+
+    /**
+     * Constructs an ELContextEvent object to indicate that an <code>ELContext</code> has been created.
+     *
+     * @param source the <code>ELContext</code> that was created.
+     */
+    public ELContextEvent(ELContext source) {
+        super(source);
+    }
+
+    /**
+     * Returns the <code>ELContext</code> that was created. This is a type-safe equivalent of the {@link #getSource} method.
+     *
+     * @return the ELContext that was created.
+     */
+    public ELContext getELContext() {
+        return (ELContext) getSource();
+    }
+}
diff --git a/api/src/main/java/javax/el/ELContextListener.java b/api/src/main/java/javax/el/ELContextListener.java
new file mode 100644
index 0000000..b6c663b
--- /dev/null
+++ b/api/src/main/java/javax/el/ELContextListener.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package javax.el;
+
+/**
+ * The listener interface for receiving notification when an {@link ELContext} is created.
+ *
+ * @see ELContext
+ * @see ELContextEvent
+ * 
+ * @since Jakarta Server Pages 2.1
+ */
+public interface ELContextListener extends java.util.EventListener {
+
+    /**
+     * Invoked when a new <code>ELContext</code> has been created.
+     *
+     * @param ece the notification event.
+     */
+    void contextCreated(ELContextEvent ece);
+
+}
diff --git a/api/src/main/java/javax/el/ELException.java b/api/src/main/java/javax/el/ELException.java
new file mode 100644
index 0000000..eb9bcbc
--- /dev/null
+++ b/api/src/main/java/javax/el/ELException.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package javax.el;
+
+/**
+ * Represents any of the exception conditions that can arise during expression evaluation.
+ *
+ * @since Jakarta Server Pages 2.1
+ */
+public class ELException extends RuntimeException {
+
+    private static final long serialVersionUID = -2161386187282690885L;
+
+    // -------------------------------------
+    /**
+     * Creates an <code>ELException</code> with no detail message.
+     */
+    public ELException() {
+        super();
+    }
+
+    // -------------------------------------
+    /**
+     * Creates an <code>ELException</code> with the provided detail message.
+     *
+     * @param pMessage the detail message
+     */
+    public ELException(String pMessage) {
+        super(pMessage);
+    }
+
+    // -------------------------------------
+    /**
+     * Creates an <code>ELException</code> with the given cause.
+     *
+     * @param pRootCause the originating cause of this exception
+     */
+    public ELException(Throwable pRootCause) {
+        super(pRootCause);
+    }
+
+    // -------------------------------------
+    /**
+     * Creates an ELException with the given detail message and root cause.
+     *
+     * @param pMessage the detail message
+     * @param pRootCause the originating cause of this exception
+     */
+    public ELException(String pMessage, Throwable pRootCause) {
+        super(pMessage, pRootCause);
+    }
+
+}
diff --git a/api/src/main/java/javax/el/ELManager.java b/api/src/main/java/javax/el/ELManager.java
new file mode 100644
index 0000000..5c56070
--- /dev/null
+++ b/api/src/main/java/javax/el/ELManager.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2013, 2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package javax.el;
+
+import java.lang.reflect.Method;
+
+/**
+ * Manages Jakarta Expression Language parsing and evaluation environment. The ELManager maintains an instance of
+ * ExpressionFactory and StandardELContext, for parsing and evaluating Jakarta Expression Language expressions.
+ *
+ * @since Jakarta Expression Language 3.0
+ */
+public class ELManager {
+
+    private StandardELContext elContext;
+
+    /**
+     * Return the ExpressionFactory instance used for Jakarta Expression Language evaluations.
+     *
+     * @return The ExpressionFactory
+     */
+    public static ExpressionFactory getExpressionFactory() {
+        return ELUtil.getExpressionFactory();
+    }
+
+    /**
+     * Return the ELContext used for parsing and evaluating Jakarta Expression Language expressions. If there is currently
+     * no ELContext, a default instance of StandardELContext is returned.
+     *
+     * @return The ELContext used for parsing and evaluating Jakarta Expression Language expressions..
+     */
+    public StandardELContext getELContext() {
+        if (elContext == null) {
+            elContext = new StandardELContext(getExpressionFactory());
+        }
+
+        return elContext;
+    }
+
+    /**
+     * Set the ELContext used for parsing and evaluating Jakarta Expression Language expressions. The supplied ELContext
+     * will not be modified, except for the context object map.
+     *
+     * @param context The new ELContext.
+     * @return The previous ELContext, null if none.
+     */
+    public ELContext setELContext(ELContext context) {
+        ELContext prevELContext = elContext;
+        elContext = new StandardELContext(context);
+        return prevELContext;
+    }
+
+    /**
+     * Register a BeanNameResolver. Construct a BeanNameELResolver with the BeanNameResolver and add it to the list of
+     * ELResolvers. Once registered, the BeanNameResolver cannot be removed.
+     *
+     * @param beanNameResolver The BeanNameResolver to be registered.
+     */
+    public void addBeanNameResolver(BeanNameResolver beanNameResolver) {
+        getELContext().addELResolver(new BeanNameELResolver(beanNameResolver));
+    }
+
+    /**
+     * Add an user defined ELResolver to the list of ELResolvers. Can be called multiple times. The new ELResolver is placed
+     * ahead of the default ELResolvers. The list of the ELResolvers added this way are ordered chronologically.
+     *
+     * @param elResolver The ELResolver to be added to the list of ELResolvers in ELContext.
+     * @see StandardELContext#addELResolver
+     */
+    public void addELResolver(ELResolver elResolver) {
+        getELContext().addELResolver(elResolver);
+    }
+
+    /**
+     * Maps a static method to Jakarta Expression Language function.
+     *
+     * @param prefix The namespace of the functions, can be "".
+     * @param function The name of the function.
+     * @param method The static method to be invoked when the function is used.
+     */
+    public void mapFunction(String prefix, String function, Method method) {
+        getELContext().getFunctionMapper().mapFunction(prefix, function, method);
+    }
+
+    /**
+     * Assign a ValueExpression to a Jakarta Expression Language variable, replacing any previous assignment to the same
+     * variable. The assignment for the variable is removed if the expression is <code>null</code>.
+     *
+     * @param variable The variable name
+     * @param expression The ValueExpression to be assigned to the variable.
+     */
+    public void setVariable(String variable, ValueExpression expression) {
+        getELContext().getVariableMapper().setVariable(variable, expression);
+    }
+
+    /**
+     * Import a static field or method. The class of the static member must be loadable from the classloader, at class
+     * resolution time.
+     *
+     * @param staticMemberName The full class name of the class to be imported
+     * @throws ELException if the name is not a full class name.
+     */
+    public void importStatic(String staticMemberName) throws ELException {
+        getELContext().getImportHandler().importStatic(staticMemberName);
+    }
+
+    /**
+     * Import a class. The imported class must be loadable from the classloader at the expression evaluation time.
+     *
+     * @param className The full class name of the class to be imported
+     * @throws ELException if the name is not a full class name.
+     */
+    public void importClass(String className) throws ELException {
+        getELContext().getImportHandler().importClass(className);
+    }
+
+    /**
+     * Import a package. At the expression evaluation time, the imported package name will be used to construct the full
+     * class name, which will then be used to load the class. Inherently, this is less efficient than importing a class.
+     *
+     * @param packageName The package name to be imported
+     */
+    public void importPackage(String packageName) {
+        getELContext().getImportHandler().importPackage(packageName);
+    }
+
+    /**
+     * Define a bean in the local bean repository
+     *
+     * @param name The name of the bean
+     * @param bean The bean instance to be defined. If null, the definition of the bean is removed.
+     * @return the previous bean (if any) mapped to <code>name</code>
+     */
+    public Object defineBean(String name, Object bean) {
+        Object previousBean = getELContext().getBeans().get(name);
+        getELContext().getBeans().put(name, bean);
+        return previousBean;
+    }
+
+    /**
+     * Register an evaluation listener.
+     *
+     * @param listener The evaluation listener to be added.
+     */
+    public void addEvaluationListener(EvaluationListener listener) {
+        getELContext().addEvaluationListener(listener);
+    }
+}
diff --git a/api/src/main/java/javax/el/ELProcessor.java b/api/src/main/java/javax/el/ELProcessor.java
new file mode 100644
index 0000000..84edb6a
--- /dev/null
+++ b/api/src/main/java/javax/el/ELProcessor.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package javax.el;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * Provides an API for using Jakarta Expression Language in a stand-alone environment.
+ *
+ * <p>
+ * This class provides a direct and simple interface for
+ * <ul>
+ * <li>Evaluating Jakarta Expression Language expressions.</li>
+ * <li>Assigning values to beans or setting a bean property.</li>
+ * <li>Setting a {@link ValueExpression} to a Jakarta Expression Language variable.</li>
+ * <li>Defining a static method as Jakarta Expression Language function.</li>
+ * <li>Defining an object instance as Jakarta Expression Language name.
+ * </ul>
+ *
+ * <p>
+ * This API is not a replacement for the APIs in Jakarta Expression Language 2.2. Containers that maintain Jakarta
+ * Expression Language environments can continue to do so, without using this API.
+ *
+ * <p>
+ * For Jakarta Expression Language users who want to manipulate Jakarta Expression Language environments, like adding
+ * custom {@link ELResolver}s, {@link ELManager} can be used.
+ *
+ * <h3>Scope and Life Cycle</h3>
+ * <p>
+ * Since it maintains the state of the Jakarta Expression Language environments, <code>ELProcessor</code> is not thread
+ * safe. In the simplest case, an instance can be created and destroyed before and after evaluating Jakarta Expression
+ * Language expressions. A more general usage is to use an instance of <code>ELProcessor</code> for a session, so that
+ * the user can configure the Jakarta Expression Language evaluation environment for that session.
+ * </p>
+ *
+ * <h3>Automatic Bracketing of Expressions</h3>
+ * <p>
+ * A note about the Jakarta Expression Language expressions strings used in the class. The strings allowed in the methods
+ * {@link ELProcessor#getValue}, {@link ELProcessor#setValue}, and {@link ELProcessor#setVariable} are limited to
+ * non-composite expressions, i.e. expressions of the form ${...} or #{...} only. Also, it is not necessary (in fact not
+ * allowed) to bracket the expression strings with ${ or #{ and } in these methods: they will be automatically
+ * bracketed. This reduces the visual cluster, without any lost of functionalities (thanks to the addition of the
+ * concatenation operator).
+ *
+ * <h3>Example</h3> The following code snippet illustrates the use of ELProcessor to define a bean and evaluate its
+ * property. <blockquote>
+ *
+ * <pre>
+ * ELProcessor elp = new ELProcessor();
+ * elp.defineBean("employee", new Employee("Charlie Brown"));
+ * String name = elp.eval("employee.name");
+ * </pre>
+ *
+ * </blockquote>
+ *
+ * @since Jakarta Expression Language 3.0
+ */
+public class ELProcessor {
+
+    private ELManager elManager = new ELManager();
+    private ExpressionFactory factory = ELManager.getExpressionFactory();
+
+    /**
+     * Return the ELManager used for Jakarta Expression Language processing.
+     *
+     * @return The ELManager used for Jakarta Expression Language processing.
+     */
+    public ELManager getELManager() {
+        return elManager;
+    }
+
+    /**
+     * Evaluates an Jakarta Expression Language expression.
+     *
+     * @param expression The Jakarta Expression Language expression to be evaluated.
+     * @return The result of the expression evaluation.
+     */
+    public Object eval(String expression) {
+        return getValue(expression, Object.class);
+    }
+
+    /**
+     * Evaluates an Jakarta Expression Language expression, and coerces the result to the specified type.
+     *
+     * @param expression The Jakarta Expression Language expression to be evaluated.
+     * @param expectedType Specifies the type that the resultant evaluation will be coerced to.
+     * @return The result of the expression evaluation.
+     */
+    public Object getValue(String expression, Class<?> expectedType) {
+        ValueExpression exp = factory.createValueExpression(elManager.getELContext(), bracket(expression), expectedType);
+        return exp.getValue(elManager.getELContext());
+    }
+
+    /**
+     * Sets an expression with a new value. The target expression is evaluated, up to the last property resolution, and the
+     * resultant (base, property) pair is set to the provided value.
+     *
+     * @param expression The target expression
+     * @param value The new value to set.
+     * 
+     * @throws PropertyNotFoundException if one of the property resolutions failed because a specified variable or property
+     * does not exist or is not readable.
+     * @throws PropertyNotWritableException if the final variable or property resolution failed because the specified
+     * variable or property is not writable.
+     * @throws ELException if an exception was thrown while attempting to set the property or variable. The thrown exception
+     * must be included as the cause property of this exception, if available.
+     */
+    public void setValue(String expression, Object value) {
+        ValueExpression exp = factory.createValueExpression(elManager.getELContext(), bracket(expression), Object.class);
+        exp.setValue(elManager.getELContext(), value);
+    }
+
+    /**
+     * Assign a Jakarta Expression Language expression to a Jakarta Expression Language variable. The expression is parsed,
+     * but not evaluated, and the parsed expression is mapped to the Jakarta Expression Language variable in the local
+     * variable map. Any previously assigned expression to the same variable will be replaced. If the expression is
+     * <code>null</code>, the variable will be removed.
+     *
+     * @param var The name of the variable.
+     * @param expression The Jakarta Expression Language expression to be assigned to the variable.
+     */
+    public void setVariable(String var, String expression) {
+        ValueExpression exp = factory.createValueExpression(elManager.getELContext(), bracket(expression), Object.class);
+        elManager.setVariable(var, exp);
+    }
+
+    /**
+     * Define a Jakarta Expression Language function in the local function mapper.
+     *
+     * @param prefix The namespace for the function or "" for no namesapce.
+     * @param function The name of the function. If empty (""), the method name is used as the function name.
+     * @param className The full Java class name that implements the function.
+     * @param method The name (specified without parenthesis) or the signature (as in the Java Language Spec) of the static
+     * method that implements the function. If the name (e.g. "sum") is given, the first declared method in class that
+     * matches the name is selected. If the signature (e.g. "int sum(int, int)" ) is given, then the declared method with
+     * the signature is selected.
+     *
+     * @throws NullPointerException if any of the arguments is null.
+     * @throws ClassNotFoundException if the specified class does not exists.
+     * @throws NoSuchMethodException if the method (with or without the signature) is not a declared method of the class, or
+     * if the method signature is not valid, or if the method is not a static method.
+     */
+    public void defineFunction(String prefix, String function, String className, String method) throws ClassNotFoundException, NoSuchMethodException {
+        if (prefix == null || function == null || className == null || method == null) {
+            throw new NullPointerException("Null argument for defineFunction");
+        }
+
+        Method meth = null;
+        ClassLoader loader = Thread.currentThread().getContextClassLoader();
+        if (loader == null) {
+            loader = getClass().getClassLoader();
+        }
+
+        Class<?> klass = Class.forName(className, false, loader);
+        int j = method.indexOf('(');
+        if (j < 0) {
+            // Just a name is given
+            for (Method m : klass.getDeclaredMethods()) {
+                if (m.getName().equals(method)) {
+                    meth = m;
+                }
+            }
+            if (meth == null) {
+                throw new NoSuchMethodException("Bad method name: " + method);
+            }
+        } else {
+            // method is the signature
+            // First get the method name, ignore the return type
+            int p = method.indexOf(' ');
+            if (p < 0) {
+                throw new NoSuchMethodException("Bad method signature: " + method);
+            }
+
+            String methodName = method.substring(p + 1, j).trim();
+
+            // Extract parameter types
+            p = method.indexOf(')', j + 1);
+            if (p < 0) {
+                throw new NoSuchMethodException("Bad method signature: " + method);
+            }
+            String[] params = method.substring(j + 1, p).split(",");
+            Class<?>[] paramTypes = new Class<?>[params.length];
+            for (int i = 0; i < params.length; i++) {
+                paramTypes[i] = toClass(params[i], loader);
+            }
+            meth = klass.getDeclaredMethod(methodName, paramTypes);
+        }
+        if (!Modifier.isStatic(meth.getModifiers())) {
+            throw new NoSuchMethodException("The method specified in defineFunction must be static: " + meth);
+        }
+
+        if (function.equals("")) {
+            function = method;
+        }
+
+        elManager.mapFunction(prefix, function, meth);
+    }
+
+    /**
+     * Define a Jakarta Expression Language function in the local function mapper.
+     *
+     * @param prefix The namespace for the function or "" for no namesapce.
+     * @param function The name of the function. If empty (""), the method name is used as the function name.
+     * @param method The <code>java.lang.reflect.Method</code> instance of the method that implements the function.
+     * 
+     * @throws NullPointerException if any of the arguments is null.
+     * @throws NoSuchMethodException if the method is not a static method
+     */
+    public void defineFunction(String prefix, String function, Method method) throws NoSuchMethodException {
+        if (prefix == null || function == null || method == null) {
+            throw new NullPointerException("Null argument for defineFunction");
+        }
+        if (!Modifier.isStatic(method.getModifiers())) {
+            throw new NoSuchMethodException("The method specified in defineFunction must be static: " + method);
+        }
+        if (function.equals("")) {
+            function = method.getName();
+        }
+
+        elManager.mapFunction(prefix, function, method);
+    }
+
+    /**
+     * Define a bean in a local bean repository, hiding other beans of the same name.
+     *
+     * @param name The name of the bean
+     * @param bean The bean instance to be defined. If <code>null</code>, the name will be removed from the local bean
+     * repository.
+     */
+    public void defineBean(String name, Object bean) {
+        elManager.defineBean(name, bean);
+    }
+
+    /**
+     * Return the Class object associated with the class or interface with the given name.
+     */
+    private static Class<?> toClass(String type, ClassLoader loader) throws ClassNotFoundException {
+        Class<?> c = null;
+        int i0 = type.indexOf('[');
+        int dims = 0;
+        if (i0 > 0) {
+            // This is an array. Count the dimensions
+            for (int i = 0; i < type.length(); i++) {
+                if (type.charAt(i) == '[') {
+                    dims++;
+                }
+            }
+            type = type.substring(0, i0);
+        }
+
+        if ("boolean".equals(type)) {
+            c = boolean.class;
+        } else if ("char".equals(type)) {
+            c = char.class;
+        } else if ("byte".equals(type)) {
+            c = byte.class;
+        } else if ("short".equals(type)) {
+            c = short.class;
+        } else if ("int".equals(type)) {
+            c = int.class;
+        } else if ("long".equals(type)) {
+            c = long.class;
+        } else if ("float".equals(type)) {
+            c = float.class;
+        } else if ("double".equals(type)) {
+            c = double.class;
+        } else {
+            c = loader.loadClass(type);
+        }
+
+        if (dims == 0) {
+            return c;
+        }
+
+        if (dims == 1) {
+            return java.lang.reflect.Array.newInstance(c, 1).getClass();
+        }
+
+        // Array of more than i dimension
+        return Array.newInstance(c, new int[dims]).getClass();
+    }
+
+    private String bracket(String expression) {
+        return "${" + expression + '}';
+    }
+}
diff --git a/api/src/main/java/javax/el/ELResolver.java b/api/src/main/java/javax/el/ELResolver.java
new file mode 100644
index 0000000..f21459d
--- /dev/null
+++ b/api/src/main/java/javax/el/ELResolver.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package javax.el;
+
+import java.beans.FeatureDescriptor;
+import java.util.Iterator;
+
+/**
+ * Enables customization of variable, property, method call, and type conversion resolution behavior for Jakarta
+ * Expression Language expression evaluation.
+ *
+ * <p>
+ * While evaluating an expression, the <code>ELResolver</code> associated with the {@link ELContext} is consulted to do
+ * the initial resolution of the first variable of an expression. It is also consulted when a <code>.</code> or
+ * <code>[]</code> operator is encountered.
+ *
+ * <p>
+ * For example, in the Jakarta Expression Language expression <code>${employee.lastName}</code>, the
+ * <code>ELResolver</code> determines what object <code>employee</code> refers to, and what it means to get the
+ * <code>lastName</code> property on that object.
+ *
+ * <p>
+ * Most methods in this class accept a <code>base</code> and <code>property</code> parameter. In the case of variable
+ * resolution (e.g. determining what <code>employee</code> refers to in <code>${employee.lastName}</code>), the
+ * <code>base</code> parameter will be <code>null</code> and the <code>property</code> parameter will always be of type
+ * <code>String</code>. In this case, if the <code>property</code> is not a <code>String</code>, the behavior of the
+ * <code>ELResolver</code> is undefined.
+ *
+ * <p>
+ * In the case of property resolution, the <code>base</code> parameter identifies the base object and the
+ * <code>property</code> object identifies the property on that base. For example, in the expression
+ * <code>${employee.lastName}</code>, <code>base</code> is the result of the variable resolution for
+ * <code>employee</code> and <code>property</code> is the string <code>"lastName"</code>. In the expression
+ * <code>${y[x]}</code>, <code>base</code> is the result of the variable resolution for <code>y</code> and
+ * <code>property</code> is the result of the variable resolution for <code>x</code>.
+ *
+ * <p>
+ * In the case of method call resolution, the <code>base</code> parameter identifies the base object and the
+ * <code>method</code> parameter identifies a method on that base. In the case of overloaded methods, the <code>
+ * paramTypes</code> parameter can be optionally used to identify a method. The <code>params</code>parameter are the
+ * parameters for the method call, and can also be used for resolving overloaded methods when the
+ * <code>paramTypes</code> parameter is not specified.
+ *
+ * <p>
+ * In the case of type conversion resolution, the <code>obj</code> parameter identifies the source object and the
+ * <code>targetType</code> parameter identifies the target type the source to covert to.
+ *
+ * <p>
+ * Though only a single <code>ELResolver</code> is associated with an <code>ELContext</code>, there are usually multiple
+ * resolvers considered for any given variable or property resolution. <code>ELResolver</code>s are combined together
+ * using {@link CompositeELResolver}s, to define rich semantics for evaluating an expression.
+ * </p>
+ *
+ * <p>
+ * For the {@link #getValue}, {@link #getType}, {@link #setValue}, and {@link #isReadOnly} methods, an
+ * <code>ELResolver</code> is not responsible for resolving all possible (base, property) pairs. In fact, most resolvers
+ * will only handle a <code>base</code> of a single type. To indicate that a resolver has successfully resolved a
+ * particular (base, property) pair, it must set the <code>propertyResolved</code> property of the
+ * <code>ELContext</code> to <code>true</code>. If it could not handle the given pair, it must leave this property
+ * alone. The caller must ignore the return value of the method if <code>propertyResolved</code> is <code>false</code>.
+ *
+ * <p>
+ * Similarly, for the {@link #convertToType} method an <code>ELResolver</code> must set the
+ * <code>propertyResolved</code> to <code>true</code> to indicate that it handles the conversion of the object to the
+ * target type.
+ *
+ * <p>
+ * The {@link #getFeatureDescriptors} and {@link #getCommonPropertyType} methods are primarily designed for design-time
+ * tool support, but must handle invocation at runtime as well. The {@link java.beans.Beans#isDesignTime} method can be
+ * used to determine if the resolver is being consulted at design-time or runtime.
+ *
+ * @see CompositeELResolver
+ * @see ELContext#getELResolver
+ * @since Jakarta Server Pages 2.1
+ */
+public abstract class ELResolver {
+
+    // --------------------------------------------------------- Constants
+
+    /**
+     * The attribute name of the named attribute in the <code>FeatureDescriptor</code> that specifies the runtime type of
+     * the variable or property.
+     */
+    public static final String TYPE = "type";
+
+    /**
+     * The attribute name of the named attribute in the <code>FeatureDescriptor</code> that specifies whether the variable
+     * or property can be resolved at runtime.
+     */
+    public static final String RESOLVABLE_AT_DESIGN_TIME = "resolvableAtDesignTime";
+
+    /**
+     * Attempts to resolve the given <code>property</code> object on the given <code>base</code> object.
+     *
+     * <p>
+     * If this resolver handles the given (base, property) pair, the <code>propertyResolved</code> property of the
+     * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is
+     * not <code>true</code> after this method is called, the caller should ignore the return value.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The base object whose property value is to be returned, or <code>null</code> to resolve a top-level
+     * variable.
+     * @param property The property or variable to be resolved.
+     * 
+     * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
+     * the result of the variable or property resolution; otherwise undefined.
+     * 
+     * @throws NullPointerException if context is <code>null</code>
+     * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but
+     * the specified variable or property does not exist or is not readable.
+     * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     */
+    public abstract Object getValue(ELContext context, Object base, Object property);
+
+    /**
+     * Attempts to resolve and invoke the given <code>method</code> on the given <code>base</code> object.
+     *
+     * <p>
+     * If this resolver handles the given (base, method) pair, the <code>propertyResolved</code> property of the
+     * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is
+     * not <code>true</code> after this method is called, the caller should ignore the return value.
+     *
+     * <p>
+     * A default implementation is provided that returns null so that existing classes that extend ELResolver can continue
+     * to function.
+     *
+     * @param context The context of this evaluation.
+     * @param base The bean on which to invoke the method
+     * @param method The simple name of the method to invoke. Will be coerced to a <code>String</code>.
+     * @param paramTypes An array of Class objects identifying the method's formal parameter types, in declared order. Use
+     * an empty array if the method has no parameters. Can be <code>null</code>, in which case the method's formal parameter
+     * types are assumed to be unknown.
+     * @param params The parameters to pass to the method, or <code>null</code> if no parameters.
+     * 
+     * @return The result of the method invocation (<code>null</code> if the method has a <code>void</code> return type).
+     * 
+     * @throws MethodNotFoundException if no suitable method can be found.
+     * @throws ELException if an exception was thrown while performing (base, method) resolution. The thrown exception must
+     * be included as the cause property of this exception, if available. If the exception thrown is an
+     * <code>InvocationTargetException</code>, extract its <code>cause</code> and pass it to the <code>ELException</code>
+     * constructor.
+     * 
+     * @since Jakarta Expression Language 2.2
+     */
+    public Object invoke(ELContext context, Object base, Object method, Class<?>[] paramTypes, Object[] params) {
+        return null;
+    }
+
+    /**
+     * For a given <code>base</code> and <code>property</code>, attempts to identify the most general type that is
+     * acceptable for an object to be passed as the <code>value</code> parameter in a future call to the {@link #setValue}
+     * method.
+     *
+     * <p>
+     * If this resolver handles the given (base, property) pair, the <code>propertyResolved</code> property of the
+     * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is
+     * not <code>true</code> after this method is called, the caller should ignore the return value.
+     * </p>
+     *
+     * <p>
+     * This is not always the same as <code>getValue().getClass()</code>. For example, in the case of an
+     * {@link ArrayELResolver}, the <code>getType</code> method will return the element type of the array, which might be a
+     * superclass of the type of the actual element that is currently in the specified array element.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The base object whose property value is to be analyzed, or <code>null</code> to analyze a top-level
+     * variable.
+     * @param property The property or variable to return the acceptable type for.
+     * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
+     * the most general acceptable type; otherwise undefined.
+     * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but
+     * the specified variable or property does not exist or is not readable.
+     * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     */
+    public abstract Class<?> getType(ELContext context, Object base, Object property);
+
+    /**
+     * Attempts to set the value of the given <code>property</code> object on the given <code>base</code> object.
+     *
+     * <p>
+     * If this resolver handles the given (base, property) pair, the <code>propertyResolved</code> property of the
+     * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is
+     * not <code>true</code> after this method is called, the caller can safely assume no value has been set.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The base object whose property value is to be set, or <code>null</code> to set a top-level variable.
+     * @param property The property or variable to be set.
+     * @param value The value to set the property or variable to.
+     * @throws NullPointerException if context is <code>null</code>
+     * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but
+     * the specified variable or property does not exist.
+     * @throws PropertyNotWritableException if the given (base, property) pair is handled by this <code>ELResolver</code>
+     * but the specified variable or property is not writable.
+     * @throws ELException if an exception was thrown while attempting to set the property or variable. The thrown exception
+     * must be included as the cause property of this exception, if available.
+     */
+    public abstract void setValue(ELContext context, Object base, Object property, Object value);
+
+    /**
+     * For a given <code>base</code> and <code>property</code>, attempts to determine whether a call to {@link #setValue}
+     * will always fail.
+     *
+     * <p>
+     * If this resolver handles the given (base, property) pair, the <code>propertyResolved</code> property of the
+     * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is
+     * not <code>true</code> after this method is called, the caller should ignore the return value.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The base object whose property value is to be analyzed, or <code>null</code> to analyze a top-level
+     * variable.
+     * @param property The property or variable to return the read-only status for.
+     * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
+     * <code>true</code> if the property is read-only or <code>false</code> if not; otherwise undefined.
+     * @throws NullPointerException if context is <code>null</code>
+     * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but
+     * the specified variable or property does not exist.
+     * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     */
+    public abstract boolean isReadOnly(ELContext context, Object base, Object property);
+
+    /**
+     * Returns information about the set of variables or properties that can be resolved for the given <code>base</code>
+     * object. One use for this method is to assist tools in auto-completion.
+     *
+     * <p>
+     * If the <code>base</code> parameter is <code>null</code>, the resolver must enumerate the list of top-level variables
+     * it can resolve.
+     * </p>
+     *
+     * <p>
+     * The <code>Iterator</code> returned must contain zero or more instances of {@link java.beans.FeatureDescriptor}, in no
+     * guaranteed order. In the case of primitive types such as <code>int</code>, the value <code>null</code> must be
+     * returned. This is to prevent the useless iteration through all possible primitive values. A return value of
+     * <code>null</code> indicates that this resolver does not handle the given <code>base</code> object or that the results
+     * are too complex to represent with this method and the {@link #getCommonPropertyType} method should be used instead.
+     * </p>
+     *
+     * <p>
+     * Each <code>FeatureDescriptor</code> will contain information about a single variable or property. In addition to the
+     * standard properties, the <code>FeatureDescriptor</code> must have two named attributes (as set by the
+     * <code>setValue</code> method):
+     * <ul>
+     * <li>{@link #TYPE} - The value of this named attribute must be an instance of <code>java.lang.Class</code> and specify
+     * the runtime type of the variable or property.</li>
+     * <li>{@link #RESOLVABLE_AT_DESIGN_TIME} - The value of this named attribute must be an instance of
+     * <code>java.lang.Boolean</code> and indicates whether it is safe to attempt to resolve this property at design-time.
+     * For instance, it may be unsafe to attempt a resolution at design time if the <code>ELResolver</code> needs access to
+     * a resource that is only available at runtime and no acceptable simulated value can be provided.</li>
+     * </ul>
+     *
+     * <p>
+     * The caller should be aware that the <code>Iterator</code> returned might iterate through a very large or even
+     * infinitely large set of properties. Care should be taken by the caller to not get stuck in an infinite loop.
+     *
+     * <p>
+     * This is a "best-effort" list. Not all <code>ELResolver</code>s will return completely accurate results, but all must
+     * be callable at both design-time and runtime (i.e. whether or not <code>Beans.isDesignTime()</code> returns
+     * <code>true</code>), without causing errors.
+     *
+     * <p>
+     * The <code>propertyResolved</code> property of the <code>ELContext</code> is not relevant to this method. The results
+     * of all <code>ELResolver</code>s are concatenated in the case of composite resolvers.
+     *
+     * @param context The context of this evaluation.
+     * @param base The base object whose set of valid properties is to be enumerated, or <code>null</code> to enumerate the
+     * set of top-level variables that this resolver can evaluate.
+     * @return An <code>Iterator</code> containing zero or more (possibly infinitely more) <code>FeatureDescriptor</code>
+     * objects, or <code>null</code> if this resolver does not handle the given <code>base</code> object or that the results
+     * are too complex to represent with this method
+     * @see java.beans.FeatureDescriptor
+     */
+    public abstract Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base);
+
+    /**
+     * Returns the most general type that this resolver accepts for the <code>property</code> argument, given a
+     * <code>base</code> object. One use for this method is to assist tools in auto-completion.
+     *
+     * <p>
+     * This assists tools in auto-completion and also provides a way to express that the resolver accepts a primitive value,
+     * such as an integer index into an array. For example, the {@link ArrayELResolver} will accept any <code>int</code> as
+     * a <code>property</code>, so the return value would be <code>Integer.class</code>.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The base object to return the most general property type for, or <code>null</code> to enumerate the set
+     * of top-level variables that this resolver can evaluate.
+     * @return <code>null</code> if this <code>ELResolver</code> does not know how to handle the given <code>base</code>
+     * object; otherwise <code>Object.class</code> if any type of <code>property</code> is accepted; otherwise the most
+     * general <code>property</code> type accepted for the given <code>base</code>.
+     */
+    public abstract Class<?> getCommonPropertyType(ELContext context, Object base);
+
+    /**
+     * Converts an object to a specific type.
+     *
+     * <p>
+     * An <code>ELException</code> is thrown if an error occurs during the conversion.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param obj The object to convert.
+     * @param targetType The target type for the conversion.
+     * @return object converted to <code>targetType</code>
+     * @throws ELException thrown if errors occur.
+     */
+    public Object convertToType(ELContext context, Object obj, Class<?> targetType) {
+        return null;
+    }
+}
diff --git a/api/src/main/java/javax/el/ELUtil.java b/api/src/main/java/javax/el/ELUtil.java
new file mode 100644
index 0000000..4df4a44
--- /dev/null
+++ b/api/src/main/java/javax/el/ELUtil.java
@@ -0,0 +1,794 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package javax.el;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * Utility methods for this portion of the Jakarta Expression Language implementation
+ *
+ * <p>
+ * Methods on this class use a Map instance stored in ThreadLocal storage to minimize the performance impact on
+ * operations that take place multiple times on a single Thread. The keys and values of the Map are implementation
+ * private.
+ *
+ * @author edburns
+ * @author Kin-man Chung
+ * @author Dongbin Nie
+ */
+class ELUtil {
+
+    /**
+     * This class may not be constructed.
+     */
+    private ELUtil() {
+    }
+
+    /*
+     * For testing Backward Compatibility option static java.util.Properties properties = new java.util.Properties(); static
+     * { properties.setProperty("javax.el.bc2.2", "true"); }
+     */
+    public static ExpressionFactory exprFactory = ExpressionFactory.newInstance(/* properties */);
+
+    /**
+     * <p>
+     * The <code>ThreadLocal</code> variable used to record the <code>javax.faces.context.FacesContext</code> instance for
+     * each processing thread.
+     * </p>
+     */
+    private static ThreadLocal<Map<String, ResourceBundle>> instance = new ThreadLocal<Map<String, ResourceBundle>>() {
+        @Override
+        protected Map<String, ResourceBundle> initialValue() {
+            return (null);
+        }
+    };
+
+    /**
+     * @return a Map stored in ThreadLocal storage. This may be used by methods of this class to minimize the performance
+     * impact for operations that may take place multiple times on a given Thread instance.
+     */
+    private static Map<String, ResourceBundle> getCurrentInstance() {
+        Map<String, ResourceBundle> result = instance.get();
+        if (result == null) {
+            result = new HashMap<>();
+            setCurrentInstance(result);
+        }
+
+        return result;
+
+    }
+
+    /**
+     * Replace the Map with the argument context.
+     *
+     * @param context the Map to be stored in ThreadLocal storage.
+     */
+    private static void setCurrentInstance(Map<String, ResourceBundle> context) {
+        instance.set(context);
+    }
+
+    /**
+     * Convenience method, calls through to getExceptionMessageString(javax.el.ELContext,java.lang.String,Object []).
+     *
+     * @param context the ELContext from which the Locale for this message is extracted.
+     * @param messageId the messageId String in the ResourceBundle
+     *
+     * @return a localized String for the argument messageId
+     */
+    public static String getExceptionMessageString(ELContext context, String messageId) {
+        return getExceptionMessageString(context, messageId, null);
+    }
+
+    /*
+     * <p>Return a Localized message String suitable for use as an Exception message. Examine the argument
+     * <code>context</code> for a <code>Locale</code>. If not present, use <code>Locale.getDefault()</code>. Load the
+     * <code>ResourceBundle</code> "javax.el.Messages" using that locale. Get the message string for argument
+     * <code>messageId</code>. If not found return "Missing Resource in Jakarta Expression Language implementation ??? messageId ???" with messageId
+     * substituted with the runtime value of argument <code>messageId</code>. If found, and argument <code>params</code> is
+     * non-null, format the message using the params. If formatting fails, return a sensible message including the
+     * <code>messageId</code>. If argument <code>params</code> is <code>null</code>, skip formatting and return the message
+     * directly, otherwise return the formatted message.</p>
+     *
+     * @param context the ELContext from which the Locale for this message is extracted.
+     *
+     * @param messageId the messageId String in the ResourceBundle
+     *
+     * @param params parameters to the message
+     *
+     * @return a localized String for the argument messageId
+     */
+    public static String getExceptionMessageString(ELContext context, String messageId, Object[] params) {
+        String result = "";
+        Locale locale = null;
+
+        if (null == context || null == messageId) {
+            return result;
+        }
+
+        if (null == (locale = context.getLocale())) {
+            locale = Locale.getDefault();
+        }
+
+        if (locale != null) {
+            Map<String, ResourceBundle> threadMap = getCurrentInstance();
+            ResourceBundle resourceBundle = null;
+            if (null == (resourceBundle = threadMap.get(locale.toString()))) {
+                resourceBundle = ResourceBundle.getBundle("javax.el.PrivateMessages", locale);
+                threadMap.put(locale.toString(), resourceBundle);
+            }
+
+            if (null != resourceBundle) {
+                try {
+                    result = resourceBundle.getString(messageId);
+                    if (null != params) {
+                        result = MessageFormat.format(result, params);
+                    }
+                } catch (IllegalArgumentException iae) {
+                    result = "Can't get localized message: parameters to message appear to be incorrect.  Message to format: " + messageId;
+                } catch (MissingResourceException mre) {
+                    result = "Missing Resource in Jakarta Expression Language implementation: ???" + messageId + "???";
+                } catch (Exception e) {
+                    result = "Exception resolving message in Jakarta Expression Language implementation: ???" + messageId + "???";
+                }
+            }
+        }
+
+        return result;
+    }
+
+    static ExpressionFactory getExpressionFactory() {
+        return exprFactory;
+    }
+
+    static Constructor<?> findConstructor(Class<?> klass, Class<?>[] paramTypes, Object[] params) {
+        String methodName = "<init>";
+
+        if (klass == null) {
+            throw new MethodNotFoundException("Method not found: " + klass + "." + methodName + "(" + paramString(paramTypes) + ")");
+        }
+
+        if (paramTypes == null) {
+            paramTypes = getTypesFromValues(params);
+        }
+
+        Constructor<?>[] constructors = klass.getConstructors();
+
+        List<Wrapper> wrappers = Wrapper.wrap(constructors);
+
+        Wrapper result = findWrapper(klass, wrappers, methodName, paramTypes, params);
+
+        if (result == null) {
+            return null;
+        }
+
+        return getConstructor(klass, (Constructor<?>) result.unWrap());
+    }
+
+    static Object invokeConstructor(ELContext context, Constructor<?> constructor, Object[] params) {
+        Object[] parameters = buildParameters(context, constructor.getParameterTypes(), constructor.isVarArgs(), params);
+        try {
+            return constructor.newInstance(parameters);
+        } catch (IllegalAccessException iae) {
+            throw new ELException(iae);
+        } catch (IllegalArgumentException iae) {
+            throw new ELException(iae);
+        } catch (InvocationTargetException ite) {
+            throw new ELException(ite.getCause());
+        } catch (InstantiationException ie) {
+            throw new ELException(ie.getCause());
+        }
+    }
+
+    static Method findMethod(Class<?> klass, String methodName, Class<?>[] paramTypes, Object[] params, boolean staticOnly) {
+        Method method = findMethod(klass, methodName, paramTypes, params);
+        if (staticOnly && !Modifier.isStatic(method.getModifiers())) {
+            throw new MethodNotFoundException("Method " + methodName + "for class " + klass + " not found or accessible");
+        }
+
+        return method;
+    }
+
+    /*
+     * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
+     */
+    static Object invokeMethod(ELContext context, Method method, Object base, Object[] params) {
+
+        Object[] parameters = buildParameters(context, method.getParameterTypes(), method.isVarArgs(), params);
+        try {
+            return method.invoke(base, parameters);
+        } catch (IllegalAccessException iae) {
+            throw new ELException(iae);
+        } catch (IllegalArgumentException iae) {
+            throw new ELException(iae);
+        } catch (InvocationTargetException ite) {
+            throw new ELException(ite.getCause());
+        }
+    }
+
+    /*
+     * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
+     */
+    static Method findMethod(Class<?> clazz, String methodName, Class<?>[] paramTypes, Object[] paramValues) {
+        if (clazz == null || methodName == null) {
+            throw new MethodNotFoundException("Method not found: " + clazz + "." + methodName + "(" + paramString(paramTypes) + ")");
+        }
+
+        if (paramTypes == null) {
+            paramTypes = getTypesFromValues(paramValues);
+        }
+
+        Method[] methods = clazz.getMethods();
+
+        List<Wrapper> wrappers = Wrapper.wrap(methods, methodName);
+
+        Wrapper result = findWrapper(clazz, wrappers, methodName, paramTypes, paramValues);
+
+        if (result == null) {
+            return null;
+        }
+
+        return getMethod(clazz, (Method) result.unWrap());
+    }
+
+    /*
+     * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
+     */
+    @SuppressWarnings("null")
+    private static Wrapper findWrapper(Class<?> clazz, List<Wrapper> wrappers, String name, Class<?>[] paramTypes, Object[] paramValues) {
+        List<Wrapper> assignableCandidates = new ArrayList<>();
+        List<Wrapper> coercibleCandidates = new ArrayList<>();
+        List<Wrapper> varArgsCandidates = new ArrayList<>();
+
+        int paramCount;
+        if (paramTypes == null) {
+            paramCount = 0;
+        } else {
+            paramCount = paramTypes.length;
+        }
+
+        for (Wrapper w : wrappers) {
+            Class<?>[] mParamTypes = w.getParameterTypes();
+            int mParamCount;
+            if (mParamTypes == null) {
+                mParamCount = 0;
+            } else {
+                mParamCount = mParamTypes.length;
+            }
+
+            // Check the number of parameters
+            if (!(paramCount == mParamCount || (w.isVarArgs() && paramCount >= mParamCount - 1))) {
+                // Method has wrong number of parameters
+                continue;
+            }
+
+            // Check the parameters match
+            boolean assignable = false;
+            boolean coercible = false;
+            boolean varArgs = false;
+            boolean noMatch = false;
+            for (int i = 0; i < mParamCount; i++) {
+                if (i == (mParamCount - 1) && w.isVarArgs()) {
+                    varArgs = true;
+                    // exact var array type match
+                    if (mParamCount == paramCount) {
+                        if (mParamTypes[i] == paramTypes[i]) {
+                            continue;
+                        }
+                    }
+
+                    // unwrap the array's component type
+                    Class<?> varType = mParamTypes[i].getComponentType();
+                    for (int j = i; j < paramCount; j++) {
+                        if (!isAssignableFrom(paramTypes[j], varType)
+                                && !(paramValues != null && j < paramValues.length && isCoercibleFrom(paramValues[j], varType))) {
+                            noMatch = true;
+                            break;
+                        }
+                    }
+                } else if (mParamTypes[i].equals(paramTypes[i])) {
+                } else if (isAssignableFrom(paramTypes[i], mParamTypes[i])) {
+                    assignable = true;
+                } else {
+                    if (paramValues == null || i >= paramValues.length) {
+                        noMatch = true;
+                        break;
+                    } else {
+                        if (isCoercibleFrom(paramValues[i], mParamTypes[i])) {
+                            coercible = true;
+                        } else {
+                            noMatch = true;
+                            break;
+                        }
+                    }
+                }
+            }
+            if (noMatch) {
+                continue;
+            }
+
+            if (varArgs) {
+                varArgsCandidates.add(w);
+            } else if (coercible) {
+                coercibleCandidates.add(w);
+            } else if (assignable) {
+                assignableCandidates.add(w);
+            } else {
+                // If a method is found where every parameter matches exactly,
+                // return it
+                return w;
+            }
+
+        }
+
+        String errorMsg = "Unable to find unambiguous method: " + clazz + "." + name + "(" + paramString(paramTypes) + ")";
+        if (!assignableCandidates.isEmpty()) {
+            return findMostSpecificWrapper(assignableCandidates, paramTypes, false, errorMsg);
+        } else if (!coercibleCandidates.isEmpty()) {
+            return findMostSpecificWrapper(coercibleCandidates, paramTypes, true, errorMsg);
+        } else if (!varArgsCandidates.isEmpty()) {
+            return findMostSpecificWrapper(varArgsCandidates, paramTypes, true, errorMsg);
+        } else {
+            throw new MethodNotFoundException("Method not found: " + clazz + "." + name + "(" + paramString(paramTypes) + ")");
+        }
+
+    }
+
+    /*
+     * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
+     */
+    private static Wrapper findMostSpecificWrapper(List<Wrapper> candidates, Class<?>[] matchingTypes, boolean elSpecific, String errorMsg) {
+        List<Wrapper> ambiguouses = new ArrayList<>();
+        for (Wrapper candidate : candidates) {
+            boolean lessSpecific = false;
+
+            Iterator<Wrapper> it = ambiguouses.iterator();
+            while (it.hasNext()) {
+                int result = isMoreSpecific(candidate, it.next(), matchingTypes, elSpecific);
+                if (result == 1) {
+                    it.remove();
+                } else if (result == -1) {
+                    lessSpecific = true;
+                }
+            }
+
+            if (!lessSpecific) {
+                ambiguouses.add(candidate);
+            }
+        }
+
+        if (ambiguouses.size() > 1) {
+            throw new MethodNotFoundException(errorMsg);
+        }
+
+        return ambiguouses.get(0);
+    }
+
+    /*
+     * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
+     */
+    private static int isMoreSpecific(Wrapper wrapper1, Wrapper wrapper2, Class<?>[] matchingTypes, boolean elSpecific) {
+        Class<?>[] paramTypes1 = wrapper1.getParameterTypes();
+        Class<?>[] paramTypes2 = wrapper2.getParameterTypes();
+
+        if (wrapper1.isVarArgs()) {
+            // JLS8 15.12.2.5 Choosing the Most Specific Method
+            int length = Math.max(Math.max(paramTypes1.length, paramTypes2.length), matchingTypes.length);
+            paramTypes1 = getComparingParamTypesForVarArgsMethod(paramTypes1, length);
+            paramTypes2 = getComparingParamTypesForVarArgsMethod(paramTypes2, length);
+
+            if (length > matchingTypes.length) {
+                Class<?>[] matchingTypes2 = new Class<?>[length];
+                System.arraycopy(matchingTypes, 0, matchingTypes2, 0, matchingTypes.length);
+                matchingTypes = matchingTypes2;
+            }
+        }
+
+        int result = 0;
+        for (int i = 0; i < paramTypes1.length; i++) {
+            if (paramTypes1[i] != paramTypes2[i]) {
+                int r2 = isMoreSpecific(paramTypes1[i], paramTypes2[i], matchingTypes[i], elSpecific);
+                if (r2 == 1) {
+                    if (result == -1) {
+                        return 0;
+                    }
+                    result = 1;
+                } else if (r2 == -1) {
+                    if (result == 1) {
+                        return 0;
+                    }
+                    result = -1;
+                } else {
+                    return 0;
+                }
+            }
+        }
+
+        if (result == 0) {
+            // The nature of bridge methods is such that it actually
+            // doesn't matter which one we pick as long as we pick
+            // one. That said, pick the 'right' one (the non-bridge
+            // one) anyway.
+            result = Boolean.compare(wrapper1.isBridge(), wrapper2.isBridge());
+        }
+
+        return result;
+    }
+
+    /*
+     * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
+     */
+    private static int isMoreSpecific(Class<?> type1, Class<?> type2, Class<?> matchingType, boolean elSpecific) {
+        type1 = getBoxingTypeIfPrimitive(type1);
+        type2 = getBoxingTypeIfPrimitive(type2);
+        if (type2.isAssignableFrom(type1)) {
+            return 1;
+        } else if (type1.isAssignableFrom(type2)) {
+            return -1;
+        } else {
+            if (elSpecific) {
+                /*
+                 * Number will be treated as more specific
+                 *
+                 * ASTInteger only return Long or BigInteger, no Byte / Short / Integer. ASTFloatingPoint also.
+                 *
+                 */
+                if (matchingType != null && Number.class.isAssignableFrom(matchingType)) {
+                    boolean b1 = Number.class.isAssignableFrom(type1) || type1.isPrimitive();
+                    boolean b2 = Number.class.isAssignableFrom(type2) || type2.isPrimitive();
+                    if (b1 && !b2) {
+                        return 1;
+                    } else if (b2 && !b1) {
+                        return -1;
+                    } else {
+                        return 0;
+                    }
+                }
+
+                return 0;
+            } else {
+                return 0;
+            }
+        }
+    }
+
+    /*
+     * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
+     */
+    private static Class<?> getBoxingTypeIfPrimitive(Class<?> clazz) {
+        if (clazz.isPrimitive()) {
+            if (clazz == Boolean.TYPE) {
+                return Boolean.class;
+            }
+            if (clazz == Character.TYPE) {
+                return Character.class;
+            }
+            if (clazz == Byte.TYPE) {
+                return Byte.class;
+            }
+            if (clazz == Short.TYPE) {
+                return Short.class;
+            }
+            if (clazz == Integer.TYPE) {
+                return Integer.class;
+            }
+            if (clazz == Long.TYPE) {
+                return Long.class;
+            }
+            if (clazz == Float.TYPE) {
+                return Float.class;
+            }
+
+            return Double.class;
+        } else {
+            return clazz;
+        }
+    }
+
+    /*
+     * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
+     */
+    private static Class<?>[] getComparingParamTypesForVarArgsMethod(Class<?>[] paramTypes, int length) {
+        Class<?>[] result = new Class<?>[length];
+        System.arraycopy(paramTypes, 0, result, 0, paramTypes.length - 1);
+        Class<?> type = paramTypes[paramTypes.length - 1].getComponentType();
+        for (int i = paramTypes.length - 1; i < length; i++) {
+            result[i] = type;
+        }
+
+        return result;
+    }
+
+    /*
+     * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
+     */
+    private static final String paramString(Class<?>[] types) {
+        if (types != null) {
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < types.length; i++) {
+                if (types[i] == null) {
+                    sb.append("null, ");
+                } else {
+                    sb.append(types[i].getName()).append(", ");
+                }
+            }
+            if (sb.length() > 2) {
+                sb.setLength(sb.length() - 2);
+            }
+            return sb.toString();
+        }
+        return null;
+    }
+
+    /*
+     * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
+     */
+    static boolean isAssignableFrom(Class<?> src, Class<?> target) {
+        // src will always be an object
+        // Short-cut. null is always assignable to an object and in Jakarta Expression Language null
+        // can always be coerced to a valid value for a primitive
+        if (src == null) {
+            return true;
+        }
+
+        target = getBoxingTypeIfPrimitive(target);
+
+        return target.isAssignableFrom(src);
+    }
+
+    /*
+     * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
+     */
+    private static boolean isCoercibleFrom(Object src, Class<?> target) {
+        // TODO: This isn't pretty but it works. Significant refactoring would
+        // be required to avoid the exception.
+        try {
+            getExpressionFactory().coerceToType(src, target);
+        } catch (Exception e) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /*
+     * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
+     */
+    private static Class<?>[] getTypesFromValues(Object[] values) {
+        if (values == null) {
+            return null;
+        }
+
+        Class<?> result[] = new Class<?>[values.length];
+        for (int i = 0; i < values.length; i++) {
+            if (values[i] == null) {
+                result[i] = null;
+            } else {
+                result[i] = values[i].getClass();
+            }
+        }
+
+        return result;
+    }
+
+    /*
+     * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
+     *
+     * Get a public method form a public class or interface of a given method. Note that if a PropertyDescriptor is obtained
+     * for a non-public class that implements a public interface, the read/write methods will be for the class, and
+     * therefore inaccessible. To correct this, a version of the same method must be found in a superclass or interface.
+     *
+     */
+    static Method getMethod(Class<?> type, Method m) {
+        if (m == null || Modifier.isPublic(type.getModifiers())) {
+            return m;
+        }
+        Class<?>[] inf = type.getInterfaces();
+        Method mp = null;
+        for (int i = 0; i < inf.length; i++) {
+            try {
+                mp = inf[i].getMethod(m.getName(), m.getParameterTypes());
+                mp = getMethod(mp.getDeclaringClass(), mp);
+                if (mp != null) {
+                    return mp;
+                }
+            } catch (NoSuchMethodException e) {
+                // Ignore
+            }
+        }
+        Class<?> sup = type.getSuperclass();
+        if (sup != null) {
+            try {
+                mp = sup.getMethod(m.getName(), m.getParameterTypes());
+                mp = getMethod(mp.getDeclaringClass(), mp);
+                if (mp != null) {
+                    return mp;
+                }
+            } catch (NoSuchMethodException e) {
+                // Ignore
+            }
+        }
+        return null;
+    }
+
+    /*
+     * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
+     */
+    static Constructor<?> getConstructor(Class<?> type, Constructor<?> c) {
+        if (c == null || Modifier.isPublic(type.getModifiers())) {
+            return c;
+        }
+        Constructor<?> cp = null;
+        Class<?> sup = type.getSuperclass();
+        if (sup != null) {
+            try {
+                cp = sup.getConstructor(c.getParameterTypes());
+                cp = getConstructor(cp.getDeclaringClass(), cp);
+                if (cp != null) {
+                    return cp;
+                }
+            } catch (NoSuchMethodException e) {
+                // Ignore
+            }
+        }
+        return null;
+    }
+
+    /*
+     * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
+     */
+    @SuppressWarnings("null")
+    static Object[] buildParameters(ELContext context, Class<?>[] parameterTypes, boolean isVarArgs, Object[] params) {
+        Object[] parameters = null;
+        if (parameterTypes.length > 0) {
+            parameters = new Object[parameterTypes.length];
+            int paramCount = params == null ? 0 : params.length;
+            if (isVarArgs) {
+                int varArgIndex = parameterTypes.length - 1;
+                // First argCount-1 parameters are standard
+                for (int i = 0; (i < varArgIndex && i < paramCount); i++) {
+                    parameters[i] = context.convertToType(params[i], parameterTypes[i]);
+                }
+                // Last parameter is the varargs
+                if (parameterTypes.length == paramCount && parameterTypes[varArgIndex] == params[varArgIndex].getClass()) {
+                    parameters[varArgIndex] = params[varArgIndex];
+                } else {
+                    Class<?> varArgClass = parameterTypes[varArgIndex].getComponentType();
+                    final Object varargs = Array.newInstance(varArgClass, (paramCount - varArgIndex));
+                    for (int i = (varArgIndex); i < paramCount; i++) {
+                        Array.set(varargs, i - varArgIndex, context.convertToType(params[i], varArgClass));
+                    }
+                    parameters[varArgIndex] = varargs;
+                }
+            } else {
+                for (int i = 0; i < parameterTypes.length && i < paramCount; i++) {
+                    parameters[i] = context.convertToType(params[i], parameterTypes[i]);
+                }
+            }
+        }
+        return parameters;
+    }
+
+    /*
+     * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
+     */
+    private abstract static class Wrapper {
+
+        public static List<Wrapper> wrap(Method[] methods, String name) {
+            List<Wrapper> result = new ArrayList<>();
+            for (Method method : methods) {
+                if (method.getName().equals(name)) {
+                    result.add(new MethodWrapper(method));
+                }
+            }
+            return result;
+        }
+
+        public static List<Wrapper> wrap(Constructor<?>[] constructors) {
+            List<Wrapper> result = new ArrayList<>();
+            for (Constructor<?> constructor : constructors) {
+                result.add(new ConstructorWrapper(constructor));
+            }
+            return result;
+        }
+
+        public abstract Object unWrap();
+
+        public abstract Class<?>[] getParameterTypes();
+
+        public abstract boolean isVarArgs();
+
+        public abstract boolean isBridge();
+    }
+
+    /*
+     * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
+     */
+    private static class MethodWrapper extends Wrapper {
+        private final Method m;
+
+        public MethodWrapper(Method m) {
+            this.m = m;
+        }
+
+        @Override
+        public Object unWrap() {
+            return m;
+        }
+
+        @Override
+        public Class<?>[] getParameterTypes() {
+            return m.getParameterTypes();
+        }
+
+        @Override
+        public boolean isVarArgs() {
+            return m.isVarArgs();
+        }
+
+        @Override
+        public boolean isBridge() {
+            return m.isBridge();
+        }
+    }
+
+    /*
+     * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync.
+     */
+    private static class ConstructorWrapper extends Wrapper {
+        private final Constructor<?> c;
+
+        public ConstructorWrapper(Constructor<?> c) {
+            this.c = c;
+        }
+
+        @Override
+        public Object unWrap() {
+            return c;
+        }
+
+        @Override
+        public Class<?>[] getParameterTypes() {
+            return c.getParameterTypes();
+        }
+
+        @Override
+        public boolean isVarArgs() {
+            return c.isVarArgs();
+        }
+
+        @Override
+        public boolean isBridge() {
+            return false;
+        }
+    }
+
+}
diff --git a/api/src/main/java/javax/el/EvaluationListener.java b/api/src/main/java/javax/el/EvaluationListener.java
new file mode 100644
index 0000000..f359ad2
--- /dev/null
+++ b/api/src/main/java/javax/el/EvaluationListener.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package javax.el;
+
+/**
+ * The listener interface for receiving notification when a Jakarta Expression Language expression is evaluated.
+ *
+ * @since Jakarta Expression Language 3.0
+ */
+public abstract class EvaluationListener {
+
+    /**
+     * Receives notification before a Jakarta Expression Language expression is evaluated
+     *
+     * @param context The ELContext
+     * @param expression The Jakarta Expression Language expression string to be evaluated
+     */
+    public void beforeEvaluation(ELContext context, String expression) {
+    }
+
+    /**
+     * Receives notification after a Jakarta Expression Language expression is evaluated
+     *
+     * @param context The ELContext
+     * @param expression The Jakarta Expression Language expression string to be evaluated
+     */
+    public void afterEvaluation(ELContext context, String expression) {
+    }
+
+    /**
+     * Receives notification when the (base, property) pair is resolved
+     *
+     * @param context The ELContext
+     * @param base The base object
+     * @param property The property object
+     */
+    public void propertyResolved(ELContext context, Object base, Object property) {
+    }
+
+}
diff --git a/api/src/main/java/javax/el/Expression.java b/api/src/main/java/javax/el/Expression.java
new file mode 100644
index 0000000..fd00f8f
--- /dev/null
+++ b/api/src/main/java/javax/el/Expression.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package javax.el;
+
+import java.io.Serializable;
+
+/**
+ * Base class for the expression subclasses {@link ValueExpression} and {@link MethodExpression}, implementing
+ * characteristics common to both.
+ *
+ * <p>
+ * All expressions must implement the <code>equals()</code> and <code>hashCode()</code> methods so that two expressions
+ * can be compared for equality. They are redefined abstract in this class to force their implementation in subclasses.
+ * </p>
+ *
+ * <p>
+ * All expressions must also be <code>Serializable</code> so that they can be saved and restored.
+ * </p>
+ *
+ * <p>
+ * <code>Expression</code>s are also designed to be immutable so that only one instance needs to be created for any
+ * given expression String / {@link FunctionMapper}. This allows a container to pre-create expressions and not have to
+ * re-parse them each time they are evaluated.
+ * </p>
+ *
+ * @since Jakarta Server Pages 2.1
+ */
+public abstract class Expression implements Serializable {
+
+    private static final long serialVersionUID = -6663767980471823812L;
+
+    /**
+     * Returns the original String used to create this <code>Expression</code>, unmodified.
+     *
+     * <p>
+     * This is used for debugging purposes but also for the purposes of comparison (e.g. to ensure the expression in a
+     * configuration file has not changed).
+     * </p>
+     *
+     * <p>
+     * This method does not provide sufficient information to re-create an expression. Two different expressions can have
+     * exactly the same expression string but different function mappings. Serialization should be used to save and restore
+     * the state of an <code>Expression</code>.
+     * </p>
+     *
+     * @return The original expression String.
+     */
+    public abstract String getExpressionString();
+
+    // Comparison
+
+    /**
+     * Determines whether the specified object is equal to this <code>Expression</code>.
+     *
+     * <p>
+     * The result is <code>true</code> if and only if the argument is not <code>null</code>, is an <code>Expression</code>
+     * object that is the of the same type (<code>ValueExpression</code> or <code>MethodExpression</code>), and has an
+     * identical parsed representation.
+     * </p>
+     *
+     * <p>
+     * Note that two expressions can be equal if their expression Strings are different. For example,
+     * <code>${fn1:foo()}</code> and <code>${fn2:foo()}</code> are equal if their corresponding <code>FunctionMapper</code>s
+     * mapped <code>fn1:foo</code> and <code>fn2:foo</code> to the same method.
+     * </p>
+     *
+     * @param obj the <code>Object</code> to test for equality.
+     * @return <code>true</code> if <code>obj</code> equals this <code>Expression</code>; <code>false</code> otherwise.
+     * @see java.util.Hashtable
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public abstract boolean equals(Object obj);
+
+    /**
+     * Returns the hash code for this <code>Expression</code>.
+     *
+     * <p>
+     * See the note in the {@link #equals} method on how two expressions can be equal if their expression Strings are
+     * different. Recall that if two objects are equal according to the <code>equals(Object)</code> method, then calling the
+     * <code>hashCode</code> method on each of the two objects must produce the same integer result. Implementations must
+     * take special note and implement <code>hashCode</code> correctly.
+     * </p>
+     *
+     * @return The hash code for this <code>Expression</code>.
+     * @see #equals
+     * @see java.util.Hashtable
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public abstract int hashCode();
+
+    /**
+     * Returns whether this expression was created from only literal text.
+     *
+     * <p>
+     * This method must return <code>true</code> if and only if the expression string this expression was created from
+     * contained no unescaped Jakarta Expression Language delimeters (<code>${...}</code> or <code>#{...}</code>).
+     *
+     * @return <code>true</code> if this expression was created from only literal text; <code>false</code> otherwise.
+     */
+    public abstract boolean isLiteralText();
+}
diff --git a/api/src/main/java/javax/el/ExpressionFactory.java b/api/src/main/java/javax/el/ExpressionFactory.java
new file mode 100644
index 0000000..e005de4
--- /dev/null
+++ b/api/src/main/java/javax/el/ExpressionFactory.java
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package javax.el;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * Provides an implementation for creating and evaluating Jakarta Expression Language expressions.
+ *
+ * <p>
+ * Classes that implement the Jakarta Expression Language expression language expose their functionality via this
+ * abstract class. An implementation supports the following functionalities.
+ *
+ * <ul>
+ *   <li>Parses a <code>String</code> into a {@link ValueExpression} or {@link MethodExpression} instance for later
+ *   evaluation.</li>
+ *   <li>Implements an <code>ELResolver</code> for query operators</li>
+ *   <li>Provides a default type coercion</li>
+ * </ul>
+ *
+ * <p>
+ * The {@link #newInstance} method can be used to obtain an instance of the implementation. Technologies such as
+ * Jakarta Server Pages and Jakarta Faces provide access to an implementation via factory methods.
+ *
+ * <p>
+ * The {@link #createValueExpression} method is used to parse expressions that evaluate to values (both l-values and
+ * r-values are supported). The {@link #createMethodExpression} method is used to parse expressions that evaluate to a
+ * reference to a method on an object.
+ *
+ * <p>
+ * Resolution of model objects is performed at evaluation time, via the {@link ELResolver} associated with the
+ * {@link ELContext} passed to the <code>ValueExpression</code> or <code>MethodExpression</code>.
+ *
+ * <p>
+ * The ELContext object also provides access to the {@link FunctionMapper} and {@link VariableMapper} to be used when
+ * parsing the expression. Jakarta Expression Language function and variable mapping is performed at parse-time, and the
+ * results are bound to the expression. Therefore, the {@link ELContext}, {@link FunctionMapper}, and
+ * {@link VariableMapper} are not stored for future use and do not have to be <code>Serializable</code>.
+ *
+ * <p>
+ * The <code>createValueExpression</code> and <code>createMethodExpression</code> methods must be thread-safe. That is,
+ * multiple threads may call these methods on the same <code>ExpressionFactory</code> object simultaneously.
+ * Implementations should synchronize access if they depend on transient state. Implementations should not, however,
+ * assume that only one object of each <code>ExpressionFactory</code> type will be instantiated; global caching should
+ * therefore be static.
+ *
+ * <p>
+ * The <code>ExpressionFactory</code> must be able to handle the following types of input for the
+ * <code>expression</code> parameter:
+ * <ul>
+ *   <li>Single expressions using the <code>${}</code> delimiter (e.g. <code>"${employee.lastName}"</code>).</li>
+ *   <li>Single expressions using the <code>#{}</code> delimiter (e.g. <code>"#{employee.lastName}"</code>).</li>
+ *   <li>Literal text containing no <code>${}</code> or <code>#{}</code> delimiters (e.g. <code>"John Doe"</code>).</li>
+ *   <li>Multiple expressions using the same delimiter (e.g. <code>"${employee.firstName}${employee.lastName}"</code> or
+ *   <code>"#{employee.firstName}#{employee.lastName}"</code>).</li>
+ *   <li>Mixed literal text and expressions using the same delimiter (e.g.
+ *   <code>"Name: ${employee.firstName} ${employee.lastName}"</code>).</li>
+ * </ul>
+ *
+ * <p>
+ * The following types of input are illegal and must cause an {@link ELException} to be thrown:
+ * <ul>
+ *   <li>Multiple expressions using different delimiters (e.g.
+ *   <code>"${employee.firstName}#{employee.lastName}"</code>).</li>
+ *   <li>Mixed literal text and expressions using different delimiters(e.g.
+ *   <code>"Name: ${employee.firstName} #{employee.lastName}"</code>).</li>
+ * </ul>
+ *
+ * @since Jakarta Server Pages 2.1
+ */
+public abstract class ExpressionFactory {
+
+    /**
+     * Creates a new instance of a <code>ExpressionFactory</code>. This method uses the following ordered lookup procedure
+     * to determine the <code>ExpressionFactory</code> implementation class to load:
+     *
+     * <ul>
+     * <li>Use the Services API (as detailed in the JAR specification). If a resource with the name of
+     * <code>META-INF/services/javax.el.ExpressionFactory</code> exists, then its first line, if present, is used as the
+     * UTF-8 encoded name of the implementation class.</li>
+     * <li>Use the properties file "lib/el.properties" in the JRE directory. If this file exists and it is readable by the
+     * <code> java.util.Properties.load(InputStream)</code> method, and it contains an entry whose key is
+     * "javax.el.ExpressionFactory", then the value of that entry is used as the name of the implementation class.</li>
+     * <li>Use the <code>javax.el.ExpressionFactory</code> system property. If a system property with this name is defined,
+     * then its value is used as the name of the implementation class.</li>
+     * <li>Use a platform default implementation.</li>
+     * </ul>
+     *
+     * @return a new <code>ExpressionFactory</code> instance
+     */
+    public static ExpressionFactory newInstance() {
+        return ExpressionFactory.newInstance(null);
+    }
+
+    /**
+     * Create a new instance of a <code>ExpressionFactory</code>, with optional properties.
+     *
+     * <p>
+     * This method uses the same lookup procedure as the one used in <code>newInstance()</code>.
+     *
+     * <p>
+     * If the argument <code>properties</code> is not null, and if the implementation contains a constructor with a single
+     * parameter of type <code>java.util.Properties</code>, then the constructor is used to create the instance.
+     *
+     * <p>
+     * Properties are optional and can be ignored by an implementation.
+     *
+     * <p>
+     * The name of a property should start with "javax.el."
+     *
+     * <p>
+     * The following are some suggested names for properties.
+     * <ul>
+     * <li>javax.el.cacheSize</li>
+     * </ul>
+     *
+     * @param properties Properties passed to the implementation. If null, then no properties.
+     *
+     * @return a new <code>ExpressionFactory</code> instance
+     */
+    public static ExpressionFactory newInstance(Properties properties) {
+        return (ExpressionFactory) FactoryFinder.find("javax.el.ExpressionFactory", "com.sun.el.ExpressionFactoryImpl", properties);
+    }
+
+    /**
+     * Parses an expression into a {@link ValueExpression} for later evaluation. Use this method for expressions that refer
+     * to values.
+     *
+     * <p>
+     * This method should perform syntactic validation of the expression. If in doing so it detects errors, it should raise
+     * an <code>ELException</code>.
+     *
+     * @param context The Jakarta Expression Language context used to parse the expression. The <code>FunctionMapper</code>
+     * and <code>VariableMapper</code> stored in the ELContext are used to resolve functions and variables found in the
+     * expression. They can be <code>null</code>, in which case functions or variables are not supported for this
+     * expression. The object returned must invoke the same functions and access the same variable mappings regardless of
+     * whether the mappings in the provided <code>FunctionMapper</code> and <code>VariableMapper</code> instances change
+     * between calling <code>ExpressionFactory.createValueExpression()</code> and any method on
+     * <code>ValueExpression</code>. Note that within Jakarta Expression Language, the ${} and #{} syntaxes are treated
+     * identically. This includes the use of VariableMapper and FunctionMapper at expression creation time. Each is invoked
+     * if not null, independent of whether the #{} or ${} syntax is used for the expression.
+     * @param expression The expression to parse
+     * @param expectedType The type the result of the expression will be coerced to after evaluation.
+     * 
+     * @return The parsed expression
+     * 
+     * @throws NullPointerException Thrown if expectedType is null.
+     * @throws ELException Thrown if there are syntactical errors in the provided expression.
+     */
+    public abstract ValueExpression createValueExpression(ELContext context, String expression, Class<?> expectedType);
+
+    /**
+     * Creates a ValueExpression that wraps an object instance.
+     *
+     * <p>
+     * This method can be used to pass any object as a ValueExpression. The wrapper ValueExpression is read only, and
+     * returns the wrapped object via its <code>getValue()</code> method, optionally coerced.
+     * </p>
+     *
+     * @param instance The object instance to be wrapped.
+     * @param expectedType The type the result of the expression will be coerced to after evaluation. There will be no
+     * coercion if it is Object.class,
+     * @throws NullPointerException Thrown if expectedType is null.
+     * @return a ValueExpression that wraps an object instance
+     */
+    public abstract ValueExpression createValueExpression(Object instance, Class<?> expectedType);
+
+    /**
+     * Parses an expression into a {@link MethodExpression} for later evaluation. Use this method for expressions that refer
+     * to methods.
+     *
+     * <p>
+     * If the expression is a String literal, a <code>MethodExpression
+     * </code> is created, which when invoked, returns the String literal, coerced to expectedReturnType. An ELException is
+     * thrown if expectedReturnType is void or if the coercion of the String literal to the expectedReturnType yields an
+     * error (see Section "1.16 Type Conversion").
+     *
+     * <p>
+     * This method should perform syntactic validation of the expression. If in doing so it detects errors, it should raise
+     * an <code>ELException</code>.
+     *
+     * @param context The Jakarta Expression Language context used to parse the expression. The <code>FunctionMapper</code>
+     * and <code>VariableMapper</code> stored in the ELContext are used to resolve functions and variables found in the
+     * expression. They can be <code>null</code>, in which case functions or variables are not supported for this
+     * expression. The object returned must invoke the same functions and access the same variable mappings regardless of
+     * whether the mappings in the provided <code>FunctionMapper</code> and <code>VariableMapper</code> instances change
+     * between calling <code>ExpressionFactory.createMethodExpression()</code> and any method on
+     * <code>MethodExpression</code>. Note that within the EL, the ${} and #{} syntaxes are treated identically. This
+     * includes the use of VariableMapper and FunctionMapper at expression creation time. Each is invoked if not null,
+     * independent of whether the #{} or ${} syntax is used for the expression.
+     * @param expression The expression to parse
+     * @param expectedReturnType The expected return type for the method to be found. After evaluating the expression, the
+     * <code>MethodExpression</code> must check that the return type of the actual method matches this type. Passing in a
+     * value of <code>null</code> indicates the caller does not care what the return type is, and the check is disabled.
+     * @param expectedParamTypes The expected parameter types for the method to be found. Must be an array with no elements
+     * if there are no parameters expected. It is illegal to pass <code>null</code>, unless the method is specified with
+     * arguments in the Jakarta Expression Language expression, in which case these arguments are used for method selection,
+     * and this parameter is ignored.
+     * 
+     * @return The parsed expression
+     * 
+     * @throws ELException Thrown if there are syntactical errors in the provided expression.
+     * @throws NullPointerException if paramTypes is <code>null</code>.
+     */
+    public abstract MethodExpression createMethodExpression(ELContext context, String expression, Class<?> expectedReturnType, Class<?>[] expectedParamTypes);
+
+    /**
+     * Coerces an object to a specific type according to the Jakarta Expression Language type conversion rules. The custom
+     * type conversions in the <code>ELResolver</code>s are not considered.
+     *
+     * <p>
+     * An <code>ELException</code> is thrown if an error results from applying the conversion rules.
+     *
+     * @param obj The object to coerce.
+     * @param targetType The target type for the coercion.
+     * 
+     * @return an object coerced to <code>targetType</code>
+     * 
+     * @throws ELException thrown if an error results from applying the conversion rules.
+     */
+    public abstract Object coerceToType(Object obj, Class<?> targetType);
+
+    /**
+     * Retrieves an ELResolver that implements the operations in collections.
+     *
+     * <p>
+     * This ELResolver resolves the method invocation on the pair (<code>base</code>, <code>property</code>) when
+     * <code>base</code> is a <code>Collection</code> or a <code>Map</code>, and <code>property</code> is the name of the
+     * operation.
+     * 
+     * <p>
+     * See the specification document for detailed descriptions of these operators, their arguments, and return values.
+     *
+     * @return The <code>ELResolver</code> that implements the Query Operators.
+     *
+     * @since Jakarta Expression Language 3.0
+     */
+    public ELResolver getStreamELResolver() {
+        return null;
+    }
+
+    /**
+     * Retrieve a function map containing a pre-configured function mapping.
+     *
+     * @return A initial map for functions, null if there is none.
+     *
+     * @since Jakarta Expression Language 3.0
+     */
+    public Map<String, Method> getInitFunctionMap() {
+        return null;
+    }
+}
diff --git a/api/src/main/java/javax/el/FactoryFinder.java b/api/src/main/java/javax/el/FactoryFinder.java
new file mode 100644
index 0000000..273ab22
--- /dev/null
+++ b/api/src/main/java/javax/el/FactoryFinder.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package javax.el;
+
+import static java.io.File.separator;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.reflect.Constructor;
+import java.util.Properties;
+
+class FactoryFinder {
+
+    /**
+     * Creates an instance of the specified class using the specified <code>ClassLoader</code> object.
+     *
+     * @exception ELException if the given class could not be found or could not be instantiated
+     */
+    private static Object newInstance(String className, ClassLoader classLoader, Properties properties) {
+        try {
+            Class<?> spiClass;
+            if (classLoader == null) {
+                spiClass = Class.forName(className);
+            } else {
+                spiClass = classLoader.loadClass(className);
+            }
+
+            if (properties != null) {
+                Constructor<?> constr = null;
+                try {
+                    constr = spiClass.getConstructor(Properties.class);
+                } catch (Exception ex) {
+                }
+
+                if (constr != null) {
+                    return constr.newInstance(properties);
+                }
+            }
+            return spiClass.newInstance();
+        } catch (ClassNotFoundException x) {
+            throw new ELException("Provider " + className + " not found", x);
+        } catch (Exception x) {
+            throw new ELException("Provider " + className + " could not be instantiated: " + x, x);
+        }
+    }
+
+    /**
+     * Finds the implementation <code>Class</code> object for the given factory name, or if that fails, finds the
+     * <code>Class</code> object for the given fallback class name. The arguments supplied must be used in order. If using
+     * the first argument is successful, the second one will not be used.
+     * <P>
+     * This method is package private so that this code can be shared.
+     *
+     * @return the <code>Class</code> object of the specified message factory; may not be <code>null</code>
+     *
+     * @param factoryId the name of the factory to find, which is a system property
+     * @param fallbackClassName the implementation class name, which is to be used only if nothing else is found;
+     * <code>null</code> to indicate that there is no fallback class name
+     * @exception ELException if there is an error
+     */
+    static Object find(String factoryId, String fallbackClassName, Properties properties) {
+        ClassLoader classLoader;
+        try {
+            classLoader = Thread.currentThread().getContextClassLoader();
+        } catch (Exception x) {
+            throw new ELException(x.toString(), x);
+        }
+
+        String serviceId = "META-INF/services/" + factoryId;
+
+        // try to find services in CLASSPATH
+        try {
+            InputStream is = null;
+            if (classLoader == null) {
+                is = ClassLoader.getSystemResourceAsStream(serviceId);
+            } else {
+                is = classLoader.getResourceAsStream(serviceId);
+            }
+
+            if (is != null) {
+                BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+
+                String factoryClassName = reader.readLine();
+                reader.close();
+
+                if (factoryClassName != null && !"".equals(factoryClassName)) {
+                    return newInstance(factoryClassName, classLoader, properties);
+                }
+            }
+        } catch (Exception ex) {
+        }
+
+        // Try to read from $java.home/lib/el.properties
+        try {
+            String javah = System.getProperty("java.home");
+            String configFileName = javah + separator + "lib" + separator + "el.properties";
+
+            File configFile = new File(configFileName);
+            if (configFile.exists()) {
+                Properties props = new Properties();
+                props.load(new FileInputStream(configFile));
+                String factoryClassName = props.getProperty(factoryId);
+
+                return newInstance(factoryClassName, classLoader, properties);
+            }
+        } catch (Exception ex) {
+        }
+
+        // Use the system property
+        try {
+            String systemProp = System.getProperty(factoryId);
+            if (systemProp != null) {
+                return newInstance(systemProp, classLoader, properties);
+            }
+        } catch (SecurityException se) {
+        }
+
+        if (fallbackClassName == null) {
+            throw new ELException("Provider for " + factoryId + " cannot be found", null);
+        }
+
+        return newInstance(fallbackClassName, classLoader, properties);
+    }
+}
diff --git a/api/src/main/java/javax/el/FunctionMapper.java b/api/src/main/java/javax/el/FunctionMapper.java
new file mode 100644
index 0000000..36e76a7
--- /dev/null
+++ b/api/src/main/java/javax/el/FunctionMapper.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package javax.el;
+
+import java.lang.reflect.Method;
+
+/**
+ * The interface to a map between Jakarta Expression Language function names and methods.
+ *
+ * <p>
+ * A <code>FunctionMapper</code> maps <code>${prefix:name()}</code> style functions to a static method that can execute
+ * that function.
+ *
+ * @since Jakarta Server Pages 2.1
+ */
+public abstract class FunctionMapper {
+
+    /**
+     * Resolves the specified prefix and local name into a <code>java.lang.Method</code>.
+     *
+     * <p>
+     * Returns <code>null</code> if no function could be found that matches the given prefix and local name.
+     * </p>
+     *
+     * @param prefix the prefix of the function, or "" if no prefix. For example, <code>"fn"</code> in
+     * <code>${fn:method()}</code>, or <code>""</code> in <code>${method()}</code>.
+     * @param localName the short name of the function. For example, <code>"method"</code> in <code>${fn:method()}</code>.
+     * @return the static method to invoke, or <code>null</code> if no match was found.
+     */
+    public abstract Method resolveFunction(String prefix, String localName);
+
+    /**
+     * Adds a static method that can be used as a function.
+     *
+     * @param prefix the prefix of the function, or "" if no prefix. For example, <code>"fn"</code> in
+     * <code>${fn:method()}</code>, or <code>""</code> in <code>${method()}</code>.
+     * @param localName the short name of the function. For example, <code>"method"</code> in <code>${fn:method()}</code>.
+     * @param meth The static method that is to be invoked, when the function is referenced. The null value causes the
+     * function to be removed from the map.
+     *
+     * @since Jakarta Expression Language 3.0
+     */
+    public void mapFunction(String prefix, String localName, Method meth) {
+    }
+}
diff --git a/api/src/main/java/javax/el/ImportHandler.java b/api/src/main/java/javax/el/ImportHandler.java
new file mode 100644
index 0000000..f81866e
--- /dev/null
+++ b/api/src/main/java/javax/el/ImportHandler.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package javax.el;
+
+import static java.lang.reflect.Modifier.isAbstract;
+import static java.lang.reflect.Modifier.isInterface;
+import static java.lang.reflect.Modifier.isPublic;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Handles imports of class names and package names. An imported package name implicitly imports all the classes in the
+ * package. A class that has been imported can be used without its package name. The name is resolved to its full
+ * (package and class) name at evaluation time.
+ */
+public class ImportHandler {
+
+    private Map<String, String> classNameMap = new HashMap<>();
+    private Map<String, Class<?>> classMap = new HashMap<>();
+    private Map<String, String> staticNameMap = new HashMap<>();
+    private HashSet<String> notAClass = new HashSet<>();
+    private List<String> packages = new ArrayList<>();
+
+    {
+        importPackage("java.lang");
+    }
+
+    /**
+     * Import a static field or method.
+     *
+     * @param name The static member name, including the full class name, to be imported
+     * @throws ELException if the name does not include a ".".
+     */
+    public void importStatic(String name) throws ELException {
+        int i = name.lastIndexOf('.');
+        if (i <= 0) {
+            throw new ELException("The name " + name + " is not a full static member name");
+        }
+
+        String memberName = name.substring(i + 1);
+        String className = name.substring(0, i);
+
+        staticNameMap.put(memberName, className);
+    }
+
+    /**
+     * Import a class.
+     *
+     * @param name The full class name of the class to be imported
+     * @throws ELException if the name does not include a ".".
+     */
+    public void importClass(String name) throws ELException {
+        int i = name.lastIndexOf('.');
+        if (i <= 0) {
+            throw new ELException("The name " + name + " is not a full class name");
+        }
+
+        String className = name.substring(i + 1);
+
+        classNameMap.put(className, name);
+    }
+
+    /**
+     * Import all the classes in a package.
+     *
+     * @param packageName The package name to be imported
+     */
+    public void importPackage(String packageName) {
+        packages.add(packageName);
+    }
+
+    /**
+     * Resolve a class name.
+     *
+     * @param name The name of the class (without package name) to be resolved.
+     * @return If the class has been imported previously, with {@link #importClass} or {@link #importPackage}, then its
+     * Class instance. Otherwise <code>null</code>.
+     * @throws ELException if the class is abstract or is an interface, or not public.
+     */
+    public Class<?> resolveClass(String name) {
+        String className = classNameMap.get(name);
+        if (className != null) {
+            return resolveClassFor(className);
+        }
+
+        for (String packageName : packages) {
+            String fullClassName = packageName + "." + name;
+            Class<?> c = resolveClassFor(fullClassName);
+            if (c != null) {
+                classNameMap.put(name, fullClassName);
+                return c;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Resolve a static field or method name.
+     *
+     * @param name The name of the member(without package and class name) to be resolved.
+     * @return If the field or method has been imported previously, with {@link #importStatic}, then the class object
+     * representing the class that declares the static field or method. Otherwise <code>null</code>.
+     * @throws ELException if the class is not public, or is abstract or is an interface.
+     */
+    public Class<?> resolveStatic(String name) {
+        String className = staticNameMap.get(name);
+        if (className != null) {
+            Class<?> c = resolveClassFor(className);
+            if (c != null) {
+                return c;
+            }
+        }
+
+        return null;
+    }
+
+    private Class<?> resolveClassFor(String className) {
+        Class<?> c = classMap.get(className);
+        if (c != null) {
+            return c;
+        }
+
+        c = getClassFor(className);
+        if (c != null) {
+            checkModifiers(c.getModifiers());
+            classMap.put(className, c);
+        }
+
+        return c;
+    }
+
+    private Class<?> getClassFor(String className) {
+        if (!notAClass.contains(className)) {
+            try {
+                return Class.forName(className, false, Thread.currentThread().getContextClassLoader());
+            } catch (ClassNotFoundException ex) {
+                notAClass.add(className);
+            }
+        }
+
+        return null;
+    }
+
+    private void checkModifiers(int modifiers) {
+        if (isAbstract(modifiers) || isInterface(modifiers) || !isPublic((modifiers))) {
+            throw new ELException("Imported class must be public, and cannot be abstract or an interface");
+        }
+    }
+}
diff --git a/api/src/main/java/javax/el/LambdaExpression.java b/api/src/main/java/javax/el/LambdaExpression.java
new file mode 100644
index 0000000..540be11
--- /dev/null
+++ b/api/src/main/java/javax/el/LambdaExpression.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package javax.el;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Encapsulates a parameterized {@link ValueExpression}.
+ *
+ * <p>
+ * A <code>LambdaExpression</code> is a representation of the Jakarta Expression Language Lambda expression syntax. It
+ * consists of a list of the formal parameters and a body, represented by a {@link ValueExpression}. The body can be any
+ * valid <code>Expression</code>, including another <code>LambdaExpression</code>.
+ *
+ * <p>
+ * A <code>LambdaExpression</code> is created when an Jakarta Expression Language expression containing a Lambda
+ * expression is evaluated.
+ *
+ * <p>
+ * A <code>LambdaExpression</code> can be invoked by calling {@link LambdaExpression#invoke}, with an
+ * {@link javax.el.ELContext} and a list of the actual arguments. Alternately, a <code>LambdaExpression</code> can be
+ * invoked without passing a <code>ELContext</code>, in which case the <code>ELContext</code> previously set by calling
+ * {@link LambdaExpression#setELContext} will be used. The evaluation of the <code>ValueExpression</code> in the body
+ * uses the {@link ELContext} to resolve references to the parameters, and to evaluate the lambda expression. The result
+ * of the evaluation is returned.
+ *
+ * @see ELContext#getLambdaArgument
+ * @see ELContext#enterLambdaScope
+ * @see ELContext#exitLambdaScope
+ */
+public class LambdaExpression {
+
+    private List<String> formalParameters = new ArrayList<>();
+    private ValueExpression expression;
+    private ELContext context;
+    // Arguments from nesting lambdas, when the body is another lambda
+    private Map<String, Object> envirArgs;
+
+    /**
+     * Creates a new LambdaExpression.
+     *
+     * @param formalParameters The list of String representing the formal parameters.
+     * @param expression The <code>ValueExpression</code> representing the body.
+     */
+    public LambdaExpression(List<String> formalParameters, ValueExpression expression) {
+        this.formalParameters = formalParameters;
+        this.expression = expression;
+        this.envirArgs = new HashMap<>();
+    }
+
+    /**
+     * Set the ELContext to use in evaluating the LambdaExpression. The ELContext must to be set prior to the invocation of
+     * the LambdaExpression, unless it is supplied with {@link LambdaExpression#invoke}.
+     *
+     * @param context The ELContext to use in evaluating the LambdaExpression.
+     */
+    public void setELContext(ELContext context) {
+        this.context = context;
+    }
+
+    /**
+     * Invoke the encapsulated Lambda expression.
+     * <p>
+     * The supplied arguments are matched, in the same order, to the formal parameters. If there are more arguments than the
+     * formal parameters, the extra arguments are ignored. If there are less arguments than the formal parameters, an
+     * <code>ELException</code> is thrown.
+     * </p>
+     *
+     * <p>
+     * The actual Lambda arguments are added to the ELContext and are available during the evaluation of the Lambda
+     * expression. They are removed after the evaluation.
+     * </p>
+     *
+     * @param elContext The ELContext used for the evaluation of the expression The ELContext set by {@link #setELContext}
+     * is ignored.
+     * @param args The arguments to invoke the Lambda expression. For calls with no arguments, an empty array must be
+     * provided. A Lambda argument can be <code>null</code>.
+     * @return The result of invoking the Lambda expression
+     * @throws ELException if not enough arguments are provided
+     * @throws NullPointerException is elContext is null
+     */
+    public Object invoke(ELContext elContext, Object... args) throws ELException {
+        int i = 0;
+        Map<String, Object> lambdaArgs = new HashMap<>();
+
+        // First get arguments injected from the outter lambda, if any
+        lambdaArgs.putAll(envirArgs);
+
+        for (String fParam : formalParameters) {
+            if (i >= args.length) {
+                throw new ELException("Expected Argument " + fParam + " missing in Lambda Expression");
+            }
+            lambdaArgs.put(fParam, args[i++]);
+        }
+
+        elContext.enterLambdaScope(lambdaArgs);
+        Object ret = expression.getValue(elContext);
+
+        // If the result of evaluating the body is another LambdaExpression,
+        // whose body has not been evaluated yet. (A LambdaExpression is
+        // evaluated iff when its invoke method is called.) The current lambda
+        // arguments may be needed in that body when it is evaluated later,
+        // after the current lambda exits. To make these arguments available
+        // then, they are injected into it.
+        if (ret instanceof LambdaExpression) {
+            ((LambdaExpression) ret).envirArgs.putAll(lambdaArgs);
+        }
+        elContext.exitLambdaScope();
+
+        return ret;
+    }
+
+    /**
+     * Invoke the encapsulated Lambda expression.
+     * <p>
+     * The supplied arguments are matched, in the same order, to the formal parameters. If there are more arguments than the
+     * formal parameters, the extra arguments are ignored. If there are less arguments than the formal parameters, an
+     * <code>ELException</code> is thrown.
+     * </p>
+     *
+     * <p>
+     * The actual Lambda arguments are added to the ELContext and are available during the evaluation of the Lambda
+     * expression. They are removed after the evaluation.
+     * </p>
+     *
+     * The ELContext set by {@link LambdaExpression#setELContext} is used in the evaluation of the lambda Expression.
+     *
+     * @param args The arguments to invoke the Lambda expression. For calls with no arguments, an empty array must be
+     * provided. A Lambda argument can be <code>null</code>.
+     * @return The result of invoking the Lambda expression
+     * @throws ELException if not enough arguments are provided
+     */
+    public Object invoke(Object... args) {
+        return invoke(context, args);
+    }
+}
diff --git a/api/src/main/java/javax/el/ListELResolver.java b/api/src/main/java/javax/el/ListELResolver.java
new file mode 100644
index 0000000..4cd3a2e
--- /dev/null
+++ b/api/src/main/java/javax/el/ListELResolver.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package javax.el;
+
+import java.beans.FeatureDescriptor;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Defines property resolution behavior on instances of {@link java.util.List}.
+ *
+ * <p>
+ * This resolver handles base objects of type <code>java.util.List</code>. It accepts any object as a property and
+ * coerces that object into an integer index into the list. The resulting value is the value in the list at that index.
+ * </p>
+ *
+ * <p>
+ * This resolver can be constructed in read-only mode, which means that {@link #isReadOnly} will always return
+ * <code>true</code> and {@link #setValue} will always throw <code>PropertyNotWritableException</code>.
+ * </p>
+ *
+ * <p>
+ * <code>ELResolver</code>s are combined together using {@link CompositeELResolver}s, to define rich semantics for
+ * evaluating an expression. See the javadocs for {@link ELResolver} for details.
+ * </p>
+ *
+ * @see CompositeELResolver
+ * @see ELResolver
+ * @see java.util.List
+ * @since Jakarta Server Pages 2.1
+ */
+public class ListELResolver extends ELResolver {
+
+    static private Class<?> theUnmodifiableListClass = Collections.unmodifiableList(new ArrayList<>()).getClass();
+    private boolean isReadOnly;
+
+    /**
+     * Creates a new read/write <code>ListELResolver</code>.
+     */
+    public ListELResolver() {
+        isReadOnly = false;
+    }
+
+    /**
+     * Creates a new <code>ListELResolver</code> whose read-only status is determined by the given parameter.
+     *
+     * @param isReadOnly <code>true</code> if this resolver cannot modify lists; <code>false</code> otherwise.
+     */
+    public ListELResolver(boolean isReadOnly) {
+        this.isReadOnly = isReadOnly;
+    }
+
+    /**
+     * If the base object is a list, returns the most general acceptable type for a value in this list.
+     *
+     * <p>
+     * If the base is a <code>List</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object
+     * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after
+     * this method is called, the caller should ignore the return value.
+     * </p>
+     *
+     * <p>
+     * Assuming the base is a <code>List</code>, this method will always return <code>Object.class</code>. This is because
+     * <code>List</code>s accept any object as an element.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The list to analyze. Only bases of type <code>List</code> are handled by this resolver.
+     * @param property The index of the element in the list to return the acceptable type for. Will be coerced into an
+     * integer, but otherwise ignored by this resolver.
+     * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
+     * the most general acceptable type; otherwise undefined.
+     * @throws PropertyNotFoundException if the given index is out of bounds for this list.
+     * @throws NullPointerException if context is <code>null</code>
+     * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     */
+    @Override
+    public Class<?> getType(ELContext context, Object base, Object property) {
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base != null && base instanceof List) {
+            context.setPropertyResolved(true);
+            List<?> list = (List<?>) base;
+            int index = toInteger(property);
+            if (index < 0 || index >= list.size()) {
+                throw new PropertyNotFoundException();
+            }
+
+            return Object.class;
+        }
+
+        return null;
+    }
+
+    /**
+     * If the base object is a list, returns the value at the given index. The index is specified by the
+     * <code>property</code> argument, and coerced into an integer. If the coercion could not be performed, an
+     * <code>IllegalArgumentException</code> is thrown. If the index is out of bounds, <code>null</code> is returned.
+     *
+     * <p>
+     * If the base is a <code>List</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object
+     * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after
+     * this method is called, the caller should ignore the return value.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The list to be analyzed. Only bases of type <code>List</code> are handled by this resolver.
+     * @param property The index of the value to be returned. Will be coerced into an integer.
+     * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
+     * the value at the given index or <code>null</code> if the index was out of bounds. Otherwise, undefined.
+     * @throws IllegalArgumentException if the property could not be coerced into an integer.
+     * @throws NullPointerException if context is <code>null</code>.
+     * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     */
+    @Override
+    public Object getValue(ELContext context, Object base, Object property) {
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base != null && base instanceof List) {
+            context.setPropertyResolved(base, property);
+            List<?> list = (List<?>) base;
+            int index = toInteger(property);
+            if (index < 0 || index >= list.size()) {
+                return null;
+            }
+
+            return list.get(index);
+        }
+
+        return null;
+    }
+
+    /**
+     * If the base object is a list, attempts to set the value at the given index with the given value. The index is
+     * specified by the <code>property</code> argument, and coerced into an integer. If the coercion could not be performed,
+     * an <code>IllegalArgumentException</code> is thrown. If the index is out of bounds, a
+     * <code>PropertyNotFoundException</code> is thrown.
+     *
+     * <p>
+     * If the base is a <code>List</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object
+     * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after
+     * this method is called, the caller can safely assume no value was set.
+     * </p>
+     *
+     * <p>
+     * If this resolver was constructed in read-only mode, this method will always throw
+     * <code>PropertyNotWritableException</code>.
+     * </p>
+     *
+     * <p>
+     * If a <code>List</code> was created using {@link java.util.Collections#unmodifiableList}, this method must throw
+     * <code>PropertyNotWritableException</code>. Unfortunately, there is no Collections API method to detect this. However,
+     * an implementation can create a prototype unmodifiable <code>List</code> and query its runtime type to see if it
+     * matches the runtime type of the base object as a workaround.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The list to be modified. Only bases of type <code>List</code> are handled by this resolver.
+     * @param property The index of the value to be set. Will be coerced into an integer.
+     * @param val The value to be set at the given index.
+     * @throws ClassCastException if the class of the specified element prevents it from being added to this list.
+     * @throws NullPointerException if context is <code>null</code>, or if the value is <code>null</code> and this
+     * <code>List</code> does not support <code>null</code> elements.
+     * @throws IllegalArgumentException if the property could not be coerced into an integer, or if some aspect of the
+     * specified element prevents it from being added to this list.
+     * @throws PropertyNotWritableException if this resolver was constructed in read-only mode, or if the set operation is
+     * not supported by the underlying list.
+     * @throws PropertyNotFoundException if the given index is out of bounds for this list.
+     * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     */
+    @Override
+    public void setValue(ELContext context, Object base, Object property, Object val) {
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base != null && base instanceof List) {
+            context.setPropertyResolved(base, property);
+            // Safe cast
+            @SuppressWarnings("unchecked")
+            List<Object> list = (List<Object>) base;
+            int index = toInteger(property);
+            if (isReadOnly) {
+                throw new PropertyNotWritableException();
+            }
+
+            try {
+                list.set(index, val);
+            } catch (UnsupportedOperationException ex) {
+                throw new PropertyNotWritableException();
+            } catch (IndexOutOfBoundsException ex) {
+                throw new PropertyNotFoundException();
+            } catch (ClassCastException ex) {
+                throw ex;
+            } catch (NullPointerException ex) {
+                throw ex;
+            } catch (IllegalArgumentException ex) {
+                throw ex;
+            }
+        }
+    }
+
+    /**
+     * If the base object is a list, returns whether a call to {@link #setValue} will always fail.
+     *
+     * <p>
+     * If the base is a <code>List</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object
+     * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after
+     * this method is called, the caller should ignore the return value.
+     * </p>
+     *
+     * <p>
+     * If this resolver was constructed in read-only mode, this method will always return <code>true</code>.
+     * </p>
+     *
+     * <p>
+     * If a <code>List</code> was created using {@link java.util.Collections#unmodifiableList}, this method must return
+     * <code>true</code>. Unfortunately, there is no Collections API method to detect this. However, an implementation can
+     * create a prototype unmodifiable <code>List</code> and query its runtime type to see if it matches the runtime type of
+     * the base object as a workaround.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The list to analyze. Only bases of type <code>List</code> are handled by this resolver.
+     * @param property The index of the element in the list to return the acceptable type for. Will be coerced into an
+     * integer, but otherwise ignored by this resolver.
+     * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
+     * <code>true</code> if calling the <code>setValue</code> method will always fail or <code>false</code> if it is
+     * possible that such a call may succeed; otherwise undefined.
+     * @throws PropertyNotFoundException if the given index is out of bounds for this list.
+     * @throws NullPointerException if context is <code>null</code>
+     * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     */
+    @Override
+    public boolean isReadOnly(ELContext context, Object base, Object property) {
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base != null && base instanceof List) {
+            context.setPropertyResolved(true);
+            List<?> list = (List<?>) base;
+            int index = toInteger(property);
+            if (index < 0 || index >= list.size()) {
+                throw new PropertyNotFoundException();
+            }
+
+            return list.getClass() == theUnmodifiableListClass || isReadOnly;
+        }
+
+        return false;
+    }
+
+    /**
+     * Always returns <code>null</code>, since there is no reason to iterate through set set of all integers.
+     *
+     * <p>
+     * The {@link #getCommonPropertyType} method returns sufficient information about what properties this resolver accepts.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The list. Only bases of type <code>List</code> are handled by this resolver.
+     * @return <code>null</code>.
+     */
+    @Override
+    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
+        return null;
+    }
+
+    /**
+     * If the base object is a list, returns the most general type that this resolver accepts for the <code>property</code>
+     * argument. Otherwise, returns <code>null</code>.
+     *
+     * <p>
+     * Assuming the base is a <code>List</code>, this method will always return <code>Integer.class</code>. This is because
+     * <code>List</code>s accept integers as their index.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The list to analyze. Only bases of type <code>List</code> are handled by this resolver.
+     * @return <code>null</code> if base is not a <code>List</code>; otherwise <code>Integer.class</code>.
+     */
+    @Override
+    public Class<?> getCommonPropertyType(ELContext context, Object base) {
+        if (base != null && base instanceof List) {
+            return Integer.class;
+        }
+
+        return null;
+    }
+
+    private int toInteger(Object p) {
+        if (p instanceof Integer) {
+            return ((Integer) p).intValue();
+        }
+        if (p instanceof Character) {
+            return ((Character) p).charValue();
+        }
+        if (p instanceof Number) {
+            return ((Number) p).intValue();
+        }
+        if (p instanceof String) {
+            return Integer.parseInt((String) p);
+        }
+        throw new IllegalArgumentException();
+    }
+}
diff --git a/api/src/main/java/javax/el/MapELResolver.java b/api/src/main/java/javax/el/MapELResolver.java
new file mode 100644
index 0000000..cca84f2
--- /dev/null
+++ b/api/src/main/java/javax/el/MapELResolver.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package javax.el;
+
+import static java.lang.Boolean.TRUE;
+
+import java.beans.FeatureDescriptor;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Defines property resolution behavior on instances of {@link java.util.Map}.
+ *
+ * <p>
+ * This resolver handles base objects of type <code>java.util.Map</code>. It accepts any object as a property and uses
+ * that object as a key in the map. The resulting value is the value in the map that is associated with that key.
+ * </p>
+ *
+ * <p>
+ * This resolver can be constructed in read-only mode, which means that {@link #isReadOnly} will always return
+ * <code>true</code> and {@link #setValue} will always throw <code>PropertyNotWritableException</code>.
+ * </p>
+ *
+ * <p>
+ * <code>ELResolver</code>s are combined together using {@link CompositeELResolver}s, to define rich semantics for
+ * evaluating an expression. See the javadocs for {@link ELResolver} for details.
+ * </p>
+ *
+ * @see CompositeELResolver
+ * @see ELResolver
+ * @see java.util.Map
+ * @since Jakarta Server Pages 2.1
+ */
+public class MapELResolver extends ELResolver {
+
+    static private Class<?> theUnmodifiableMapClass = Collections.unmodifiableMap(new HashMap<>()).getClass();
+    private boolean isReadOnly;
+
+    /**
+     * Creates a new read/write <code>MapELResolver</code>.
+     */
+    public MapELResolver() {
+        isReadOnly = false;
+    }
+
+    /**
+     * Creates a new <code>MapELResolver</code> whose read-only status is determined by the given parameter.
+     *
+     * @param isReadOnly <code>true</code> if this resolver cannot modify maps; <code>false</code> otherwise.
+     */
+    public MapELResolver(boolean isReadOnly) {
+        this.isReadOnly = isReadOnly;
+    }
+
+    /**
+     * If the base object is a map, returns the most general acceptable type for a value in this map.
+     *
+     * <p>
+     * If the base is a <code>Map</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object
+     * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after
+     * this method is called, the caller should ignore the return value.
+     * </p>
+     *
+     * <p>
+     * Assuming the base is a <code>Map</code>, this method will always return <code>Object.class</code>. This is because
+     * <code>Map</code>s accept any object as the value for a given key.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The map to analyze. Only bases of type <code>Map</code> are handled by this resolver.
+     * @param property The key to return the acceptable type for. Ignored by this resolver.
+     * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
+     * the most general acceptable type; otherwise undefined.
+     * @throws NullPointerException if context is <code>null</code>
+     * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     */
+    @Override
+    public Class<?> getType(ELContext context, Object base, Object property) {
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base != null && base instanceof Map) {
+            context.setPropertyResolved(true);
+            return Object.class;
+        }
+
+        return null;
+    }
+
+    /**
+     * If the base object is a map, returns the value associated with the given key, as specified by the
+     * <code>property</code> argument. If the key was not found, <code>null</code> is returned.
+     *
+     * <p>
+     * If the base is a <code>Map</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object
+     * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after
+     * this method is called, the caller should ignore the return value.
+     * </p>
+     *
+     * <p>
+     * Just as in {@link java.util.Map#get}, just because <code>null</code> is returned doesn't mean there is no mapping for
+     * the key; it's also possible that the <code>Map</code> explicitly maps the key to <code>null</code>.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The map to be analyzed. Only bases of type <code>Map</code> are handled by this resolver.
+     * @param property The key whose associated value is to be returned.
+     * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
+     * the value associated with the given key or <code>null</code> if the key was not found. Otherwise, undefined.
+     * @throws ClassCastException if the key is of an inappropriate type for this map (optionally thrown by the underlying
+     * <code>Map</code>).
+     * @throws NullPointerException if context is <code>null</code>, or if the key is null and this map does not permit null
+     * keys (the latter is optionally thrown by the underlying <code>Map</code>).
+     * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     */
+    @Override
+    public Object getValue(ELContext context, Object base, Object property) {
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base != null && base instanceof Map) {
+            context.setPropertyResolved(base, property);
+            Map<?, ?> map = (Map<?, ?>) base;
+            return map.get(property);
+        }
+
+        return null;
+    }
+
+    /**
+     * If the base object is a map, attempts to set the value associated with the given key, as specified by the
+     * <code>property</code> argument.
+     *
+     * <p>
+     * If the base is a <code>Map</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object
+     * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after
+     * this method is called, the caller can safely assume no value was set.
+     * </p>
+     *
+     * <p>
+     * If this resolver was constructed in read-only mode, this method will always throw
+     * <code>PropertyNotWritableException</code>.
+     * </p>
+     *
+     * <p>
+     * If a <code>Map</code> was created using {@link java.util.Collections#unmodifiableMap}, this method must throw
+     * <code>PropertyNotWritableException</code>. Unfortunately, there is no Collections API method to detect this. However,
+     * an implementation can create a prototype unmodifiable <code>Map</code> and query its runtime type to see if it
+     * matches the runtime type of the base object as a workaround.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The map to be modified. Only bases of type <code>Map</code> are handled by this resolver.
+     * @param property The key with which the specified value is to be associated.
+     * @param val The value to be associated with the specified key.
+     * @throws ClassCastException if the class of the specified key or value prevents it from being stored in this map.
+     * @throws NullPointerException if context is <code>null</code>, or if this map does not permit <code>null</code> keys
+     * or values, and the specified key or value is <code>null</code>.
+     * @throws IllegalArgumentException if some aspect of this key or value prevents it from being stored in this map.
+     * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     * @throws PropertyNotWritableException if this resolver was constructed in read-only mode, or if the put operation is
+     * not supported by the underlying map.
+     */
+    @Override
+    public void setValue(ELContext context, Object base, Object property, Object val) {
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base != null && base instanceof Map) {
+            context.setPropertyResolved(base, property);
+
+            // The cast is safe
+            @SuppressWarnings("unchecked")
+            Map<Object, Object> map = (Map<Object, Object>) base;
+            if (isReadOnly || map.getClass() == theUnmodifiableMapClass) {
+                throw new PropertyNotWritableException();
+            }
+
+            try {
+                map.put(property, val);
+            } catch (UnsupportedOperationException ex) {
+                throw new PropertyNotWritableException();
+            }
+        }
+    }
+
+    /**
+     * If the base object is a map, returns whether a call to {@link #setValue} will always fail.
+     *
+     * <p>
+     * If the base is a <code>Map</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object
+     * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after
+     * this method is called, the caller should ignore the return value.
+     * </p>
+     *
+     * <p>
+     * If this resolver was constructed in read-only mode, this method will always return <code>true</code>.
+     * </p>
+     *
+     * <p>
+     * If a <code>Map</code> was created using {@link java.util.Collections#unmodifiableMap}, this method must return
+     * <code>true</code>. Unfortunately, there is no Collections API method to detect this. However, an implementation can
+     * create a prototype unmodifiable <code>Map</code> and query its runtime type to see if it matches the runtime type of
+     * the base object as a workaround.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The map to analyze. Only bases of type <code>Map</code> are handled by this resolver.
+     * @param property The key to return the read-only status for. Ignored by this resolver.
+     * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
+     * <code>true</code> if calling the <code>setValue</code> method will always fail or <code>false</code> if it is
+     * possible that such a call may succeed; otherwise undefined.
+     * @throws NullPointerException if context is <code>null</code>
+     * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     */
+    @Override
+    public boolean isReadOnly(ELContext context, Object base, Object property) {
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base != null && base instanceof Map) {
+            context.setPropertyResolved(true);
+            Map<?, ?> map = (Map<?, ?>) base;
+            return isReadOnly || map.getClass() == theUnmodifiableMapClass;
+        }
+
+        return false;
+    }
+
+    /**
+     * If the base object is a map, returns an <code>Iterator</code> containing the set of keys available in the
+     * <code>Map</code>. Otherwise, returns <code>null</code>.
+     *
+     * <p>
+     * The <code>Iterator</code> returned must contain zero or more instances of {@link java.beans.FeatureDescriptor}. Each
+     * info object contains information about a key in the Map, and is initialized as follows:
+     * <ul>
+     * <li>displayName - The return value of calling the <code>toString</code> method on this key, or <code>"null"</code> if
+     * the key is <code>null</code>.</li>
+     * <li>name - Same as displayName property.</li>
+     * <li>shortDescription - Empty string</li>
+     * <li>expert - <code>false</code></li>
+     * <li>hidden - <code>false</code></li>
+     * <li>preferred - <code>true</code></li>
+     * </ul>
+     *
+     * In addition, the following named attributes must be set in the returned <code>FeatureDescriptor</code>s:
+     * <ul>
+     * <li>{@link ELResolver#TYPE} - The return value of calling the <code>getClass()</code> method on this key, or
+     * <code>null</code> if the key is <code>null</code>.</li>
+     * <li>{@link ELResolver#RESOLVABLE_AT_DESIGN_TIME} - <code>true</code></li>
+     * </ul>
+     *
+     *
+     * @param context The context of this evaluation.
+     * @param base The map whose keys are to be iterated over. Only bases of type <code>Map</code> are handled by this
+     * resolver.
+     * @return An <code>Iterator</code> containing zero or more (possibly infinitely more) <code>FeatureDescriptor</code>
+     * objects, each representing a key in this map, or <code>null</code> if the base object is not a map.
+     */
+    @Override
+    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
+        if (base != null && base instanceof Map) {
+            Map<?, ?> map = (Map<?, ?>) base;
+            Iterator<?> iter = map.keySet().iterator();
+            List<FeatureDescriptor> list = new ArrayList<>();
+
+            while (iter.hasNext()) {
+                Object key = iter.next();
+                FeatureDescriptor descriptor = new FeatureDescriptor();
+                String name = key == null ? null : key.toString();
+                descriptor.setName(name);
+                descriptor.setDisplayName(name);
+                descriptor.setShortDescription("");
+                descriptor.setExpert(false);
+                descriptor.setHidden(false);
+                descriptor.setPreferred(true);
+
+                if (key != null) {
+                    descriptor.setValue("type", key.getClass());
+                }
+
+                descriptor.setValue("resolvableAtDesignTime", TRUE);
+                list.add(descriptor);
+            }
+
+            return list.iterator();
+        }
+
+        return null;
+    }
+
+    /**
+     * If the base object is a map, returns the most general type that this resolver accepts for the <code>property</code>
+     * argument. Otherwise, returns <code>null</code>.
+     *
+     * <p>
+     * Assuming the base is a <code>Map</code>, this method will always return <code>Object.class</code>. This is because
+     * <code>Map</code>s accept any object as a key.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The map to analyze. Only bases of type <code>Map</code> are handled by this resolver.
+     * @return <code>null</code> if base is not a <code>Map</code>; otherwise <code>Object.class</code>.
+     */
+    @Override
+    public Class<?> getCommonPropertyType(ELContext context, Object base) {
+        if (base != null && base instanceof Map) {
+            return Object.class;
+        }
+
+        return null;
+    }
+
+}
diff --git a/api/src/main/java/javax/el/MethodExpression.java b/api/src/main/java/javax/el/MethodExpression.java
new file mode 100644
index 0000000..38b02e5
--- /dev/null
+++ b/api/src/main/java/javax/el/MethodExpression.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package javax.el;
+
+/**
+ * An <code>Expression</code> that refers to a method on an object.
+ *
+ * <p>
+ * The {@link javax.el.ExpressionFactory#createMethodExpression} method can be used to parse an expression string and
+ * return a concrete instance of <code>MethodExpression</code> that encapsulates the parsed expression. The
+ * {@link FunctionMapper} is used at parse time, not evaluation time, so one is not needed to evaluate an expression
+ * using this class. However, the {@link ELContext} is needed at evaluation time.
+ * </p>
+ *
+ * <p>
+ * The {@link #getMethodInfo} and {@link #invoke} methods will evaluate the expression each time they are called. The
+ * {@link ELResolver} in the <code>ELContext</code> is used to resolve the top-level variables and to determine the
+ * behavior of the <code>.</code> and <code>[]</code> operators. For any of the two methods, the
+ * {@link javax.el.ELResolver#getValue} method is used to resolve all properties up to but excluding the last one. This
+ * provides the <code>base</code> object on which the method appears. If the <code>base</code> object is null, a
+ * <code>PropertyNotFoundException</code> must be thrown. At the last resolution, the final <code>property</code> is
+ * then coerced to a <code>String</code>, which provides the name of the method to be found. A method matching the name
+ * and expected parameters provided at parse time is found and it is either queried or invoked (depending on the method
+ * called on this <code>MethodExpression</code>).
+ * </p>
+ *
+ * <p>
+ * See the notes about comparison, serialization and immutability in the {@link Expression} javadocs.
+ *
+ * @see ELResolver
+ * @see Expression
+ * @see ExpressionFactory
+ * @since Jakarta Server Pages 2.1
+ */
+public abstract class MethodExpression extends Expression {
+
+    private static final long serialVersionUID = -1151639017737837708L;
+
+    // Evaluation
+
+    /**
+     * Evaluates the expression relative to the provided context, and returns information about the actual referenced
+     * method.
+     *
+     * @param context The context of this evaluation
+     * @return an instance of <code>MethodInfo</code> containing information about the method the expression evaluated to.
+     * @throws NullPointerException if context is <code>null</code>
+     * @throws PropertyNotFoundException if one of the property resolutions failed because a specified variable or property
+     * does not exist or is not readable.
+     * @throws MethodNotFoundException if no suitable method can be found.
+     * @throws ELException if an exception was thrown while performing property or variable resolution. The thrown exception
+     * must be included as the cause property of this exception, if available.
+     */
+    public abstract MethodInfo getMethodInfo(ELContext context);
+
+    /**
+     * If a String literal is specified as the expression, returns the String literal coerced to the expected return type of
+     * the method signature. An <code>ELException</code> is thrown if <code>expectedReturnType</code> is void or if the
+     * coercion of the String literal to the <code>expectedReturnType</code> yields an error (see Section "1.18 Type
+     * Conversion" of the Jakarta Expression Language specification).
+     *
+     * If not a String literal, evaluates the expression relative to the provided context, invokes the method that was found
+     * using the supplied parameters, and returns the result of the method invocation.
+     *
+     * Any parameters passed to this method is ignored if isLiteralText() or isParmetersProvided() is true.
+     *
+     * @param context The context of this evaluation.
+     * @param params The parameters to pass to the method, or <code>null</code> if no parameters.
+     * @return the result of the method invocation (<code>null</code> if the method has a <code>void</code> return type).
+     * @throws NullPointerException if context is <code>null</code>
+     * @throws PropertyNotFoundException if one of the property resolutions failed because a specified variable or property
+     * does not exist or is not readable.
+     * @throws MethodNotFoundException if no suitable method can be found.
+     * @throws ELException if a String literal is specified and expectedReturnType of the MethodExpression is void or if the
+     * coercion of the String literal to the expectedReturnType yields an error (see Section "1.18 Type Conversion").
+     * @throws ELException if an exception was thrown while performing property or variable resolution. The thrown exception
+     * must be included as the cause property of this exception, if available. If the exception thrown is an
+     * <code>InvocationTargetException</code>, extract its <code>cause</code> and pass it to the <code>ELException</code>
+     * constructor.
+     */
+    public abstract Object invoke(ELContext context, Object[] params);
+
+    /**
+     * Return whether this MethodExpression was created with parameters.
+     *
+     * <p>
+     * This method must return <code>true</code> if and only if parameters are specified in the EL, using the
+     * expr-a.expr-b(...) syntax.
+     *
+     * @return <code>true</code> if the MethodExpression was created with parameters, <code>false</code> otherwise.
+     * 
+     * @since Jakarta Expression Language 2.2
+     */
+    public boolean isParametersProvided() {
+        return false;
+    }
+
+    /**
+     * Use isParametersProvided instead.
+     *
+     * @return <code>true</code> if the MethodExpression was created with parameters, <code>false</code> otherwise.
+     */
+    @Deprecated
+    public boolean isParmetersProvided() {
+        return isParametersProvided();
+    }
+}
diff --git a/api/src/main/java/javax/el/MethodInfo.java b/api/src/main/java/javax/el/MethodInfo.java
new file mode 100644
index 0000000..b79f077
--- /dev/null
+++ b/api/src/main/java/javax/el/MethodInfo.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package javax.el;
+
+/**
+ * Holds information about a method that a {@link MethodExpression} evaluated to.
+ *
+ * @since Jakarta Server Pages 2.1
+ */
+public class MethodInfo {
+
+    private String name;
+    private Class<?> returnType;
+    private Class<?>[] paramTypes;
+
+    /**
+     * Creates a new instance of <code>MethodInfo</code> with the given information.
+     *
+     * @param name The name of the method
+     * @param returnType The return type of the method
+     * @param paramTypes The types of each of the method's parameters
+     */
+    public MethodInfo(String name, Class<?> returnType, Class<?>[] paramTypes) {
+        this.name = name;
+        this.returnType = returnType;
+        this.paramTypes = paramTypes;
+    }
+
+    /**
+     * Returns the name of the method
+     *
+     * @return the name of the method
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Returns the return type of the method
+     *
+     * @return the return type of the method
+     */
+    public Class<?> getReturnType() {
+        return returnType;
+    }
+
+    /**
+     * Returns the parameter types of the method
+     *
+     * @return the parameter types of the method
+     */
+    public Class<?>[] getParamTypes() {
+        return paramTypes;
+    }
+
+}
diff --git a/api/src/main/java/javax/el/MethodNotFoundException.java b/api/src/main/java/javax/el/MethodNotFoundException.java
new file mode 100644
index 0000000..a306094
--- /dev/null
+++ b/api/src/main/java/javax/el/MethodNotFoundException.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package javax.el;
+
+/**
+ * Thrown when a method could not be found while evaluating a {@link MethodExpression}.
+ *
+ * @see MethodExpression
+ * @since Jakarta Server Pages 2.1
+ */
+public class MethodNotFoundException extends ELException {
+
+    private static final long serialVersionUID = 7727548537051164640L;
+
+    /**
+     * Creates a <code>MethodNotFoundException</code> with no detail message.
+     */
+    public MethodNotFoundException() {
+        super();
+    }
+
+    /**
+     * Creates a <code>MethodNotFoundException</code> with the provided detail message.
+     *
+     * @param message the detail message
+     */
+    public MethodNotFoundException(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates a <code>MethodNotFoundException</code> with the given root cause.
+     *
+     * @param exception the originating cause of this exception
+     */
+    public MethodNotFoundException(Throwable exception) {
+        super(exception);
+    }
+
+    /**
+     * Creates a <code>MethodNotFoundException</code> with the given detail message and root cause.
+     *
+     * @param pMessage the detail message
+     * @param pRootCause the originating cause of this exception
+     */
+    public MethodNotFoundException(String pMessage, Throwable pRootCause) {
+        super(pMessage, pRootCause);
+    }
+}
diff --git a/api/src/main/java/javax/el/PrivateMessages.properties b/api/src/main/java/javax/el/PrivateMessages.properties
new file mode 100644
index 0000000..e628977
--- /dev/null
+++ b/api/src/main/java/javax/el/PrivateMessages.properties
@@ -0,0 +1,27 @@
+#
+# Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+# Copyright 2004 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# This properties file is essentially "package private" but because
+# there is no way to attach an access specifier to a properties file we
+# are including this comment to serve as such.
+setPropertyFailed=Can''t set property ''{0}'' on class ''{1}'' to value ''{2}''.
+propertyNotFound=The class ''{0}'' does not have the property ''{1}''.
+propertyNotReadable=The class ''{0}'' does not have a readable property ''{1}''.
+resolverNotWritable=The ELResolver for the class ''{0}'' is not writable.
+propertyNotWritable=The class ''{0}'' does not have a writable property ''{1}''.
+staticFieldReadError=Either ''{1}'' is not a public static field of the class ''{0}'' or field is inaccessible
+staticFieldWriteError=Cannot write to the field ''{1}}'' of the class ''{0}''
diff --git a/api/src/main/java/javax/el/PropertyNotFoundException.java b/api/src/main/java/javax/el/PropertyNotFoundException.java
new file mode 100644
index 0000000..8a829e1
--- /dev/null
+++ b/api/src/main/java/javax/el/PropertyNotFoundException.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package javax.el;
+
+/**
+ * Thrown when a property could not be found while evaluating a {@link ValueExpression} or {@link MethodExpression}.
+ *
+ * <p>
+ * For example, this could be triggered by an index out of bounds while setting an array value, or by an unreadable
+ * property while getting the value of a JavaBeans property.
+ * </p>
+ *
+ * @since Jakarta Server Pages 2.1
+ */
+public class PropertyNotFoundException extends ELException {
+
+    private static final long serialVersionUID = 7876728153282609955L;
+
+    // -------------------------------------
+    /**
+     * Creates a <code>PropertyNotFoundException</code> with no detail message.
+     */
+    public PropertyNotFoundException() {
+        super();
+    }
+
+    // -------------------------------------
+    /**
+     * Creates a <code>PropertyNotFoundException</code> with the provided detail message.
+     *
+     * @param message the detail message
+     */
+    public PropertyNotFoundException(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates a <code>PropertyNotFoundException</code> with the given root cause.
+     *
+     * @param exception the originating cause of this exception
+     */
+    public PropertyNotFoundException(Throwable exception) {
+        super(exception);
+    }
+
+    /**
+     * Creates a <code>PropertyNotFoundException</code> with the given detail message and root cause.
+     *
+     * @param pMessage the detail message
+     * @param pRootCause the originating cause of this exception
+     */
+    public PropertyNotFoundException(String pMessage, Throwable pRootCause) {
+        super(pMessage, pRootCause);
+    }
+
+}
diff --git a/api/src/main/java/javax/el/PropertyNotWritableException.java b/api/src/main/java/javax/el/PropertyNotWritableException.java
new file mode 100644
index 0000000..e814168
--- /dev/null
+++ b/api/src/main/java/javax/el/PropertyNotWritableException.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package javax.el;
+
+/**
+ * Thrown when a property could not be written to while setting the value on a {@link ValueExpression}.
+ *
+ * <p>
+ * For example, this could be triggered by trying to set a map value on an unmodifiable map.
+ * </p>
+ *
+ * @since Jakarta Server Pages 2.1
+ */
+public class PropertyNotWritableException extends ELException {
+
+    private static final long serialVersionUID = 4511862414551151572L;
+
+    /**
+     * Creates a <code>PropertyNotWritableException</code> with no detail message.
+     */
+    public PropertyNotWritableException() {
+        super();
+    }
+
+    /**
+     * Creates a <code>PropertyNotWritableException</code> with the provided detail message.
+     *
+     * @param pMessage the detail message
+     */
+    public PropertyNotWritableException(String pMessage) {
+        super(pMessage);
+    }
+
+    /**
+     * Creates a <code>PropertyNotWritableException</code> with the given root cause.
+     *
+     * @param exception the originating cause of this exception
+     */
+    public PropertyNotWritableException(Throwable exception) {
+        super(exception);
+    }
+
+    /**
+     * Creates a <code>PropertyNotWritableException</code> with the given detail message and root cause.
+     *
+     * @param pMessage the detail message
+     * @param pRootCause the originating cause of this exception
+     */
+    public PropertyNotWritableException(String pMessage, Throwable pRootCause) {
+        super(pMessage, pRootCause);
+    }
+
+}
diff --git a/api/src/main/java/javax/el/ResourceBundleELResolver.java b/api/src/main/java/javax/el/ResourceBundleELResolver.java
new file mode 100644
index 0000000..cd5e02f
--- /dev/null
+++ b/api/src/main/java/javax/el/ResourceBundleELResolver.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package javax.el;
+
+import static java.lang.Boolean.TRUE;
+
+import java.beans.FeatureDescriptor;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * Defines property resolution behavior on instances of {@link java.util.ResourceBundle}.
+ *
+ * <p>
+ * This resolver handles base objects of type <code>java.util.ResourceBundle</code>. It accepts any object as a property
+ * and coerces it to a <code>java.lang.String</code> for invoking
+ * {@link java.util.ResourceBundle#getObject(java.lang.String)}.
+ *
+ * <p>
+ * This resolver is read only and will throw a {@link PropertyNotWritableException} if <code>setValue</code> is called.
+ *
+ * <p>
+ * <code>ELResolver</code>s are combined together using {@link CompositeELResolver}s, to define rich semantics for
+ * evaluating an expression. See the javadocs for {@link ELResolver} for details.
+ *
+ * @see CompositeELResolver
+ * @see ELResolver
+ * @see java.util.ResourceBundle
+ * 
+ * @since Jakarta Server Pages 2.1
+ */
+public class ResourceBundleELResolver extends ELResolver {
+
+    /**
+     * If the base object is an instance of <code>ResourceBundle</code>, the provided property will first be coerced to a
+     * <code>String</code>. The <code>Object</code> returned by <code>getObject</code> on the base
+     * <code>ResourceBundle</code> will be returned.
+     *
+     * <p>
+     * If the base is <code>ResourceBundle</code>, the <code>propertyResolved</code> property of the <code>ELContext</code>
+     * object must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code>
+     * after this method is called, the caller should ignore the return value.
+     *
+     * @param context The context of this evaluation.
+     * @param base The ResourceBundle to analyze.
+     * @param property The name of the property to analyze. Will be coerced to a <code>String</code>.
+     * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
+     * <code>null</code> if property is <code>null</code>; otherwise the <code>Object</code> for the given key (property
+     * coerced to <code>String</code>) from the <code>ResourceBundle</code>. If no object for the given key can be found,
+     * then the <code>String</code> "???" + key + "???".
+     * @throws NullPointerException if context is <code>null</code>
+     * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown
+     * exception must be included as the cause property of this exception, if available.
+     */
+    @Override
+    public Object getValue(ELContext context, Object base, Object property) {
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base instanceof ResourceBundle) {
+            context.setPropertyResolved(true);
+            if (property != null) {
+                try {
+                    return ((ResourceBundle) base).getObject(property.toString());
+                } catch (MissingResourceException e) {
+                    return "???" + property + "???";
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * If the base object is an instance of <code>ResourceBundle</code>, return <code>null</code>, since the resolver is
+     * read only.
+     *
+     * <p>
+     * If the base is <code>ResourceBundle</code>, the <code>propertyResolved</code> property of the <code>ELContext</code>
+     * object must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code>
+     * after this method is called, the caller should ignore the return value.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base The ResourceBundle to analyze.
+     * @param property The name of the property to analyze.
+     * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
+     * <code>null</code>; otherwise undefined.
+     * @throws NullPointerException if context is <code>null</code>
+     */
+    @Override
+    public Class<?> getType(ELContext context, Object base, Object property) {
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base instanceof ResourceBundle) {
+            context.setPropertyResolved(true);
+        }
+
+        return null;
+    }
+
+    /**
+     * If the base object is a ResourceBundle, throw a {@link PropertyNotWritableException}.
+     *
+     * @param context The context of this evaluation.
+     * @param base The ResourceBundle to be modified. Only bases that are of type ResourceBundle are handled.
+     * @param property The String property to use.
+     * @param value The value to be set.
+     * @throws NullPointerException if context is <code>null</code>.
+     * @throws PropertyNotWritableException Always thrown if base is an instance of ReasourceBundle.
+     */
+    @Override
+    public void setValue(ELContext context, Object base, Object property, Object value) {
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base instanceof ResourceBundle) {
+            context.setPropertyResolved(true);
+            throw new PropertyNotWritableException("ResourceBundles are immutable");
+        }
+    }
+
+    /**
+     * If the base object is not null and an <code>instanceof</code> {@link ResourceBundle}, return <code>true</code>.
+     *
+     * @param context The context of this evaluation.
+     * @param base The ResourceBundle to be modified. Only bases that are of type ResourceBundle are handled.
+     * @param property The String property to use.
+     * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
+     * <code>true</code>; otherwise undefined.
+     * @throws NullPointerException if context is <code>null</code>
+     */
+    @Override
+    public boolean isReadOnly(ELContext context, Object base, Object property) {
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base instanceof ResourceBundle) {
+            context.setPropertyResolved(true);
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * If the base object is a ResourceBundle, returns an <code>Iterator</code> containing the set of keys available in the
+     * <code>ResourceBundle</code>. Otherwise, returns <code>null</code>.
+     *
+     * <p>
+     * The <code>Iterator</code> returned must contain zero or more instances of {@link java.beans.FeatureDescriptor}. Each
+     * info object contains information about a key in the ResourceBundle, and is initialized as follows:
+     * <ul>
+     * <li>displayName - The <code>String</code> key
+     * <li>name - Same as displayName property.</li>
+     * <li>shortDescription - Empty string</li>
+     * <li>expert - <code>false</code></li>
+     * <li>hidden - <code>false</code></li>
+     * <li>preferred - <code>true</code></li>
+     * </ul>
+     *
+     * In addition, the following named attributes must be set in the returned <code>FeatureDescriptor</code>s:
+     * <ul>
+     * <li>{@link ELResolver#TYPE} - <code>String.class</code></li>
+     * <li>{@link ELResolver#RESOLVABLE_AT_DESIGN_TIME} - <code>true</code></li>
+     * </ul>
+     *
+     *
+     * @param context The context of this evaluation.
+     * @param base The bundle whose keys are to be iterated over. Only bases of type <code>ResourceBundle</code> are handled
+     * by this resolver.
+     * @return An <code>Iterator</code> containing zero or more (possibly infinitely more) <code>FeatureDescriptor</code>
+     * objects, each representing a key in this bundle, or <code>null</code> if the base object is not a ResourceBundle.
+     */
+    @Override
+    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
+        if (base instanceof ResourceBundle) {
+            ResourceBundle bundle = (ResourceBundle) base;
+            List<FeatureDescriptor> features = new ArrayList<>();
+            String key = null;
+            FeatureDescriptor desc = null;
+
+            for (Enumeration<String> e = bundle.getKeys(); e.hasMoreElements();) {
+                key = e.nextElement();
+                desc = new FeatureDescriptor();
+                desc.setDisplayName(key);
+                desc.setExpert(false);
+                desc.setHidden(false);
+                desc.setName(key);
+                desc.setPreferred(true);
+                desc.setValue(TYPE, String.class);
+                desc.setValue(RESOLVABLE_AT_DESIGN_TIME, TRUE);
+                features.add(desc);
+            }
+
+            return features.iterator();
+        }
+
+        return null;
+    }
+
+    /**
+     * If the base object is a ResourceBundle, returns the most general type that this resolver accepts for the
+     * <code>property</code> argument. Otherwise, returns <code>null</code>.
+     *
+     * <p>
+     * Assuming the base is a <code>ResourceBundle</code>, this method will always return <code>String.class</code>.
+     *
+     * @param context The context of this evaluation.
+     * @param base The bundle to analyze. Only bases of type <code>ResourceBundle</code> are handled by this resolver.
+     * @return <code>null</code> if base is not a <code>ResourceBundle</code>; otherwise <code>String.class</code>.
+     */
+    @Override
+    public Class<?> getCommonPropertyType(ELContext context, Object base) {
+        if (base instanceof ResourceBundle) {
+            return String.class;
+        }
+
+        return null;
+    }
+}
diff --git a/api/src/main/java/javax/el/StandardELContext.java b/api/src/main/java/javax/el/StandardELContext.java
new file mode 100644
index 0000000..1c17be7
--- /dev/null
+++ b/api/src/main/java/javax/el/StandardELContext.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package javax.el;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A standard ELContext suitable for use in a stand alone environment. This class provides a default implementation of
+ * an ELResolver that contains a number of useful ELResolvers. It also provides local repositories for the
+ * FunctionMapper, VariableMapper, and BeanNameResolver.
+ *
+ * @since Jakarta Expression Language 3.0
+ */
+public class StandardELContext extends ELContext {
+
+    /*
+     * The ELResolver for this ELContext.
+     */
+    private ELResolver elResolver;
+
+    /*
+     * The list of the custom ELResolvers added to the ELResolvers. An ELResolver is added to the list when addELResolver is
+     * called.
+     */
+    private CompositeELResolver customResolvers;
+
+    /*
+     * The ELResolver implementing the query operators.
+     */
+    private ELResolver streamELResolver;
+
+    /*
+     * The FunctionMapper for this ELContext.
+     */
+    private FunctionMapper functionMapper;
+
+    /*
+     * The pre-configured init function map;
+     */
+    private Map<String, Method> initFunctionMap;
+
+    /*
+     * The VariableMapper for this ELContext.
+     */
+    private VariableMapper variableMapper;
+
+    /*
+     * If non-null, indicates the presence of a delegate ELContext. When a Standard is constructed from another ELContext,
+     * there is no easy way to get its private context map, therefore delegation is needed.
+     */
+    private ELContext delegate;
+
+    /**
+     * A bean repository local to this context
+     */
+    private Map<String, Object> beans = new HashMap<>();
+
+    /**
+     * Construct a default ELContext for a stand-alone environment.
+     *
+     * @param factory The ExpressionFactory
+     */
+    public StandardELContext(ExpressionFactory factory) {
+        streamELResolver = factory.getStreamELResolver();
+        initFunctionMap = factory.getInitFunctionMap();
+    }
+
+    /**
+     * Construct a StandardELContext from another ELContext.
+     *
+     * @param context The ELContext that acts as a delegate in most cases
+     */
+    public StandardELContext(ELContext context) {
+        delegate = context;
+
+        // Copy all attributes except map and resolved
+        CompositeELResolver compositeELResolver = new CompositeELResolver();
+        compositeELResolver.add(new BeanNameELResolver(new LocalBeanNameResolver()));
+        customResolvers = new CompositeELResolver();
+
+        compositeELResolver.add(customResolvers);
+        compositeELResolver.add(context.getELResolver());
+        elResolver = compositeELResolver;
+
+        functionMapper = context.getFunctionMapper();
+        variableMapper = context.getVariableMapper();
+        setLocale(context.getLocale());
+    }
+
+    @Override
+    public void putContext(Class key, Object contextObject) {
+        if (delegate != null) {
+            delegate.putContext(key, contextObject);
+        } else {
+            super.putContext(key, contextObject);
+        }
+    }
+
+    @Override
+    public Object getContext(Class key) {
+        if (delegate == null) {
+            return super.getContext(key);
+        }
+
+        return delegate.getContext(key);
+    }
+
+    /**
+     * Construct (if needed) and return a default ELResolver.
+     *
+     * <p>
+     * Retrieves the <code>ELResolver</code> associated with this context. This is a <code>CompositeELResover</code>
+     * consists of an ordered list of <code>ELResolver</code>s.
+     *
+     * <ol>
+     * <li>A {@link BeanNameELResolver} for beans defined locally</li>
+     * <li>Any custom <code>ELResolver</code>s</li>
+     * <li>An <code>ELResolver</code> supporting the collection operations</li>
+     * <li>A {@link StaticFieldELResolver} for resolving static fields</li>
+     * <li>A {@link MapELResolver} for resolving Map properties</li>
+     * <li>A {@link ResourceBundleELResolver} for resolving ResourceBundle properties</li>
+     * <li>A {@link ListELResolver} for resolving List properties</li>
+     * <li>An {@link ArrayELResolver} for resolving array properties</li>
+     * <li>A {@link BeanELResolver} for resolving bean properties</li>
+     * </ol>
+     *
+     * @return The ELResolver for this context.
+     */
+    @Override
+    public ELResolver getELResolver() {
+        if (elResolver == null) {
+            CompositeELResolver resolver = new CompositeELResolver();
+            customResolvers = new CompositeELResolver();
+            resolver.add(customResolvers);
+            resolver.add(new BeanNameELResolver(new LocalBeanNameResolver()));
+            if (streamELResolver != null) {
+                resolver.add(streamELResolver);
+            }
+            resolver.add(new StaticFieldELResolver());
+            resolver.add(new MapELResolver());
+            resolver.add(new ResourceBundleELResolver());
+            resolver.add(new ListELResolver());
+            resolver.add(new ArrayELResolver());
+            resolver.add(new BeanELResolver());
+            elResolver = resolver;
+        }
+
+        return elResolver;
+    }
+
+    /**
+     * Add a custom ELResolver to the context. The list of the custom ELResolvers will be accessed in the order they are
+     * added. A custom ELResolver added to the context cannot be removed.
+     *
+     * @param cELResolver The new ELResolver to be added to the context
+     */
+    public void addELResolver(ELResolver cELResolver) {
+        getELResolver(); // make sure elResolver is constructed
+        customResolvers.add(cELResolver);
+    }
+
+    /**
+     * Get the local bean repository
+     *
+     * @return the bean repository
+     */
+    Map<String, Object> getBeans() {
+        return beans;
+    }
+
+    /**
+     * Construct (if needed) and return a default FunctionMapper.
+     *
+     * @return The default FunctionMapper
+     */
+    @Override
+    public FunctionMapper getFunctionMapper() {
+        if (functionMapper == null) {
+            functionMapper = new DefaultFunctionMapper(initFunctionMap);
+        }
+
+        return functionMapper;
+    }
+
+    /**
+     * Construct (if needed) and return a default VariableMapper() {
+     *
+     * @return The default Variable
+     */
+    @Override
+    public VariableMapper getVariableMapper() {
+        if (variableMapper == null) {
+            variableMapper = new DefaultVariableMapper();
+        }
+
+        return variableMapper;
+    }
+
+    private static class DefaultFunctionMapper extends FunctionMapper {
+
+        private Map<String, Method> functions;
+
+        DefaultFunctionMapper(Map<String, Method> initMap) {
+            functions = (initMap == null) ? new HashMap<String, Method>() : new HashMap<>(initMap);
+        }
+
+        @Override
+        public Method resolveFunction(String prefix, String localName) {
+            return functions.get(prefix + ":" + localName);
+        }
+
+        @Override
+        public void mapFunction(String prefix, String localName, Method meth) {
+            functions.put(prefix + ":" + localName, meth);
+        }
+    }
+
+    private static class DefaultVariableMapper extends VariableMapper {
+
+        private Map<String, ValueExpression> variables;
+
+        @Override
+        public ValueExpression resolveVariable(String variable) {
+            if (variables == null) {
+                return null;
+            }
+
+            return variables.get(variable);
+        }
+
+        @Override
+        public ValueExpression setVariable(String variable, ValueExpression expression) {
+            if (variables == null) {
+                variables = new HashMap<>();
+            }
+
+            ValueExpression prev = null;
+            if (expression == null) {
+                prev = variables.remove(variable);
+            } else {
+                prev = variables.put(variable, expression);
+            }
+
+            return prev;
+        }
+    }
+
+    private class LocalBeanNameResolver extends BeanNameResolver {
+
+        @Override
+        public boolean isNameResolved(String beanName) {
+            return beans.containsKey(beanName);
+        }
+
+        @Override
+        public Object getBean(String beanName) {
+            return beans.get(beanName);
+        }
+
+        @Override
+        public void setBeanValue(String beanName, Object value) {
+            beans.put(beanName, value);
+        }
+
+        @Override
+        public boolean isReadOnly(String beanName) {
+            return false;
+        }
+
+        @Override
+        public boolean canCreateBean(String beanName) {
+            return true;
+        }
+    }
+}
diff --git a/api/src/main/java/javax/el/StaticFieldELResolver.java b/api/src/main/java/javax/el/StaticFieldELResolver.java
new file mode 100644
index 0000000..a945e0d
--- /dev/null
+++ b/api/src/main/java/javax/el/StaticFieldELResolver.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package javax.el;
+
+import static java.lang.reflect.Modifier.isPublic;
+import static java.lang.reflect.Modifier.isStatic;
+import static javax.el.ELUtil.getExceptionMessageString;
+
+import java.beans.FeatureDescriptor;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Iterator;
+
+/**
+ * An {@link ELResolver} for resolving static fields, enum constants and static methods. Also handles constructor calls
+ * as a special case.
+ * 
+ * <p>
+ * The resolver handles base objects of the type {@link ELClass}, which is usually generated by a Jakarta Expression
+ * Language implementation.
+ *
+ * @see ELClass
+ * @since Jakarta Expression Language 3.0
+ */
+public class StaticFieldELResolver extends ELResolver {
+
+    /**
+     * <p>
+     * Returns the value of a static field.
+     * </p>
+     * <p>
+     * If the base object is an instance of <code>ELClass</code> and the property is String, the
+     * <code>propertyResolved</code> property of the <code>ELContext</code> object must be set to <code>true</code> by this
+     * resolver, before returning. If this property is not <code>true</code> after this method is called, the caller should
+     * ignore the return value.
+     * </p>
+     *
+     * If the property is a public static field of class specified in <code>ELClass</code>, return the value of the static
+     * field. An Enum constant is a public static field of an Enum object, and is a special case of this.
+     *
+     * @param context The context of this evaluation.
+     * @param base An <code>ELClass</code>.
+     * @param property A static field name.
+     * 
+     * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
+     * the static field value.
+     * 
+     * @throws NullPointerException if context is <code>null</code>.
+     * @throws PropertyNotFoundException if the specified class does not exist, or if the field is not a public static filed
+     * of the class, or if the field is inaccessible.
+     */
+    @Override
+    public Object getValue(ELContext context, Object base, Object property) {
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base instanceof ELClass && property instanceof String) {
+            Class<?> klass = ((ELClass) base).getKlass();
+            String fieldName = (String) property;
+            try {
+                context.setPropertyResolved(base, property);
+                Field field = klass.getField(fieldName);
+
+                int mod = field.getModifiers();
+                if (Modifier.isPublic(mod) && Modifier.isStatic(mod)) {
+                    return field.get(null);
+                }
+            } catch (NoSuchFieldException ex) {
+            } catch (IllegalAccessException ex) {
+            }
+
+            throw new PropertyNotFoundException(ELUtil.getExceptionMessageString(context, "staticFieldReadError", new Object[] { klass.getName(), fieldName }));
+        }
+
+        return null;
+    }
+
+    /**
+     * <p>
+     * Attempts to write to a static field.
+     * </p>
+     * <p>
+     * If the base object is an instance of <code>ELClass</code>and the property is String, a
+     * <code>PropertyNotWritableException</code> will always be thrown, because writing to a static field is not allowed.
+     *
+     * @param context The context of this evaluation.
+     * @param base An <code>ELClass</code>
+     * @param property The name of the field
+     * @param value The value to set the field of the class to.
+     * @throws NullPointerException if context is <code>null</code>
+     * @throws PropertyNotWritableException if base object instance of <code>ELClass</code>and <code>property</code>
+     * instance of String
+     */
+    @Override
+    public void setValue(ELContext context, Object base, Object property, Object value) {
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base instanceof ELClass && property instanceof String) {
+            Class<?> klass = ((ELClass) base).getKlass();
+            String fieldName = (String) property;
+            throw new PropertyNotWritableException(
+                    getExceptionMessageString(context, "staticFieldWriteError", new Object[] { klass.getName(), fieldName }));
+        }
+    }
+
+    /**
+     * Invokes a public static method or the constructor for a class.
+     *
+     * <p>
+     * If the base object is an instance of <code>ELClass</code> and the method is a String, the
+     * <code>propertyResolved</code> property of the <code>ELContext</code> object must be set to <code>true</code> by the
+     * resolver, before returning. If this property is not <code>true</code> after this method is called, the caller should
+     * ignore the return value.
+     *
+     * <p>
+     * Invoke the public static method specified by <code>method</code>.
+     *
+     * <p>
+     * The process involved in the method selection is the same as that used in {@link BeanELResolver}.
+     *
+     * <p>
+     * As a special case, if the name of the method is "&lt;init&gt;", the constructor for the class will be invoked.
+     *
+     * @param base An <code>ELClass</code>
+     * @param methodName When coerced to a <code>String</code>, the simple name of the method.
+     * @param paramTypes An array of Class objects identifying the method's formal parameter types, in declared order. Use
+     * an empty array if the method has no parameters. Can be <code>null</code>, in which case the method's formal parameter
+     * types are assumed to be unknown.
+     * @param params The parameters to pass to the method, or <code>null</code> if no parameters.
+     * @return The result of the method invocation (<code>null</code> if the method has a <code>void</code> return type).
+     * @throws MethodNotFoundException if no suitable method can be found.
+     * @throws ELException if an exception was thrown while performing (base, method) resolution. The thrown exception must
+     * be included as the cause property of this exception, if available. If the exception thrown is an
+     * <code>InvocationTargetException</code>, extract its <code>cause</code> and pass it to the <code>ELException</code>
+     * constructor.
+     */
+    @Override
+    public Object invoke(ELContext context, Object base, Object methodName, Class<?>[] paramTypes, Object[] params) {
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (!(base instanceof ELClass && methodName instanceof String)) {
+            return null;
+        }
+
+        Class<?> klass = ((ELClass) base).getKlass();
+        String name = (String) methodName;
+
+        Object ret;
+        if ("<init>".equals(name)) {
+            Constructor<?> constructor = ELUtil.findConstructor(klass, paramTypes, params);
+            ret = ELUtil.invokeConstructor(context, constructor, params);
+        } else {
+            Method method = ELUtil.findMethod(klass, name, paramTypes, params, true);
+            ret = ELUtil.invokeMethod(context, method, null, params);
+        }
+        context.setPropertyResolved(base, methodName);
+
+        return ret;
+    }
+
+    /**
+     * Returns the type of a static field.
+     *
+     * <p>
+     * If the base object is an instance of <code>ELClass</code>and the property is a String, the
+     * <code>propertyResolved</code> property of the <code>ELContext</code> object must be set to <code>true</code> by the
+     * resolver, before returning. If this property is not <code>true</code> after this method is called, the caller can
+     * safely assume no value has been set.
+     *
+     * <p>
+     * If the property string is a public static field of class specified in ELClass, return the type of the static field.
+     *
+     * @param context The context of this evaluation.
+     * @param base An <code>ELClass</code>.
+     * @param property The name of the field.
+     * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then
+     * the type of the type of the field.
+     * @throws NullPointerException if context is <code>null</code>.
+     * @throws PropertyNotFoundException if field is not a public static filed of the class, or if the field is
+     * inaccessible.
+     */
+    @Override
+    public Class<?> getType(ELContext context, Object base, Object property) {
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base instanceof ELClass && property instanceof String) {
+            Class<?> klass = ((ELClass) base).getKlass();
+            String fieldName = (String) property;
+            try {
+                context.setPropertyResolved(true);
+                Field field = klass.getField(fieldName);
+
+                int mod = field.getModifiers();
+                if (isPublic(mod) && isStatic(mod)) {
+                    return field.getType();
+                }
+            } catch (NoSuchFieldException ex) {
+            }
+            throw new PropertyNotFoundException(getExceptionMessageString(context, "staticFieldReadError", new Object[] { klass.getName(), fieldName }));
+        }
+
+        return null;
+    }
+
+    /**
+     * <p>
+     * Inquires whether the static field is writable.
+     * </p>
+     * <p>
+     * If the base object is an instance of <code>ELClass</code>and the property is a String, the
+     * <code>propertyResolved</code> property of the <code>ELContext</code> object must be set to <code>true</code> by the
+     * resolver, before returning. If this property is not <code>true</code> after this method is called, the caller can
+     * safely assume no value has been set.
+     * </p>
+     *
+     * <p>
+     * Always returns a <code>true</code> because writing to a static field is not allowed.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param base An <code>ELClass</code>.
+     * @param property The name of the bean.
+     * @return <code>true</code>
+     * @throws NullPointerException if context is <code>null</code>.
+     */
+    @Override
+    public boolean isReadOnly(ELContext context, Object base, Object property) {
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base instanceof ELClass && property instanceof String) {
+            ((ELClass) base).getKlass();
+            context.setPropertyResolved(true);
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns the properties that can be resolved. Always returns <code>null</code>, since there is no reason to iterate
+     * through a list of one element: field name.
+     *
+     * @param context The context of this evaluation.
+     * @param base An <code>ELClass</code>.
+     * @return <code>null</code>.
+     */
+    @Override
+    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
+        return null;
+    }
+
+    /**
+     * Returns the type of the property. Always returns <code>String.class</code>, since a field name is a String.
+     *
+     * @param context The context of this evaluation.
+     * @param base An <code>ELClass</code>.
+     * @return <code>String.class</code>.
+     */
+    @Override
+    public Class<?> getCommonPropertyType(ELContext context, Object base) {
+        return String.class;
+    }
+}
diff --git a/api/src/main/java/javax/el/TypeConverter.java b/api/src/main/java/javax/el/TypeConverter.java
new file mode 100644
index 0000000..e9b3dcd
--- /dev/null
+++ b/api/src/main/java/javax/el/TypeConverter.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package javax.el;
+
+import java.beans.FeatureDescriptor;
+import java.util.Iterator;
+
+/**
+ * A convenient class for writing an ELResolver to do custom type conversions.
+ *
+ * <p>
+ * For example, to convert a String to an instance of MyDate, one can write
+ *
+ * <pre>
+ * <code>
+ *     ELProcessor elp = new ELProcessor();
+ *     elp.getELManager().addELResolver(new TypeConverter() {
+ *         Object convertToType(ELContext context, Object obj, Class&lt;?&gt; type) {
+ *             if ((obj instanceof String) &amp;&amp; type == MyDate.class) {
+ *                 context.setPropertyResolved(obj, type);
+ *                 return (obj == null)? null: new MyDate(obj.toString());
+ *             }
+ *             return null;
+ *         }
+ *      };
+ * </code>
+ * </pre>
+ *
+ * @since Jakarta Expression Language 3.0
+ */
+public abstract class TypeConverter extends ELResolver {
+
+    @Override
+    public Object getValue(ELContext context, Object base, Object property) {
+        return null;
+    }
+
+    @Override
+    public Class<?> getType(ELContext context, Object base, Object property) {
+        return null;
+    }
+
+    @Override
+    public void setValue(ELContext context, Object base, Object property, Object value) {
+    }
+
+    @Override
+    public boolean isReadOnly(ELContext context, Object base, Object property) {
+        return false;
+    }
+
+    @Override
+    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
+        return null;
+    }
+
+    @Override
+    public Class<?> getCommonPropertyType(ELContext context, Object base) {
+        return null;
+    }
+
+    /**
+     * Converts an object to a specific type.
+     *
+     * <p>
+     * An <code>ELException</code> is thrown if an error occurs during the conversion.
+     * </p>
+     *
+     * @param context The context of this evaluation.
+     * @param obj The object to convert.
+     * @param targetType The target type for the conversion.
+     * @throws ELException thrown if errors occur.
+     */
+    @Override
+    abstract public Object convertToType(ELContext context, Object obj, Class<?> targetType);
+}
diff --git a/api/src/main/java/javax/el/ValueExpression.java b/api/src/main/java/javax/el/ValueExpression.java
new file mode 100644
index 0000000..7f9cfdc
--- /dev/null
+++ b/api/src/main/java/javax/el/ValueExpression.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package javax.el;
+
+/**
+ * An <code>Expression</code> that can get or set a value.
+ *
+ * <p>
+ * In previous incarnations of this API, expressions could only be read. <code>ValueExpression</code> objects can now be
+ * used both to retrieve a value and to set a value. Expressions that can have a value set on them are referred to as
+ * l-value expressions. Those that cannot are referred to as r-value expressions. Not all r-value expressions can be
+ * used as l-value expressions (e.g. <code>"${1+1}"</code> or <code>"${firstName} ${lastName}"</code>). See the EL
+ * Specification for details. Expressions that cannot be used as l-values must always return <code>true</code> from
+ * <code>isReadOnly()</code>.
+ * </p>
+ *
+ * <p>
+ * The <code>{@link ExpressionFactory#createValueExpression}</code> method can be used to parse an expression string and
+ * return a concrete instance of <code>ValueExpression</code> that encapsulates the parsed expression. The
+ * {@link FunctionMapper} is used at parse time, not evaluation time, so one is not needed to evaluate an expression
+ * using this class. However, the {@link ELContext} is needed at evaluation time.
+ * </p>
+ *
+ * <p>
+ * The {@link #getValue}, {@link #setValue}, {@link #isReadOnly}, {@link #getType} and {@link #getValueReference}
+ * methods will evaluate the expression each time they are called. The {@link ELResolver} in the <code>ELContext</code>
+ * is used to resolve the top-level variables and to determine the behavior of the <code>.</code> and <code>[]</code>
+ * operators. For any of the five methods, the {@link ELResolver#getValue} method is used to resolve all properties up
+ * to but excluding the last one. This provides the <code>base</code> object. For all methods other than the
+ * {@link #getValueReference} method, at the last resolution, the <code>ValueExpression</code> will call the
+ * corresponding {@link ELResolver#getValue}, {@link ELResolver#setValue}, {@link ELResolver#isReadOnly} or
+ * {@link ELResolver#getType} method, depending on which was called on the <code>ValueExpression</code>. For the
+ * {@link #getValueReference} method, the (base, property) is not resolved by the ELResolver, but an instance of
+ * {@link ValueReference} is created to encapsulate this (base ,property), and returned.
+ * </p>
+ *
+ * <p>
+ * See the notes about comparison, serialization and immutability in the {@link Expression} javadocs.
+ *
+ * @see ELResolver
+ * @see Expression
+ * @see ExpressionFactory
+ * 
+ * @since Jakarta Server Pages 2.1
+ */
+public abstract class ValueExpression extends Expression {
+
+    private static final long serialVersionUID = -8466802188968516519L;
+
+    /**
+     * Evaluates the expression relative to the provided context, and returns the resulting value.
+     *
+     * <p>
+     * The resulting value is automatically coerced to the type returned by <code>getExpectedType()</code>, which was
+     * provided to the <code>ExpressionFactory</code> when this expression was created.
+     *
+     * @param context The context of this evaluation.
+     * 
+     * @return The result of the expression evaluation.
+     * 
+     * @throws NullPointerException if context is <code>null</code>.
+     * @throws PropertyNotFoundException if one of the property resolutions failed because a specified variable or property
+     * does not exist or is not readable.
+     * @throws ELException if an exception was thrown while performing property or variable resolution. The thrown exception
+     * must be included as the cause property of this exception, if available.
+     */
+    public abstract Object getValue(ELContext context);
+
+    /**
+     * Evaluates the expression relative to the provided context, and sets the result to the provided value.
+     *
+     * @param context The context of this evaluation.
+     * @param value The new value to be set.
+     * 
+     * @throws NullPointerException if context is <code>null</code>.
+     * @throws PropertyNotFoundException if one of the property resolutions failed because a specified variable or property
+     * does not exist or is not readable.
+     * @throws PropertyNotWritableException if the final variable or property resolution failed because the specified
+     * variable or property is not writable.
+     * @throws ELException if an exception was thrown while attempting to set the property or variable. The thrown exception
+     * must be included as the cause property of this exception, if available.
+     */
+    public abstract void setValue(ELContext context, Object value);
+
+    /**
+     * Evaluates the expression relative to the provided context, and returns <code>true</code> if a call to
+     * {@link #setValue} will always fail.
+     *
+     * @param context The context of this evaluation.
+     * 
+     * @return <code>true</code> if the expression is read-only or <code>false</code> if not.
+     * 
+     * @throws NullPointerException if context is <code>null</code>.
+     * @throws PropertyNotFoundException if one of the property resolutions failed because a specified variable or property
+     * does not exist or is not readable.
+     * @throws ELException if an exception was thrown while performing property or variable resolution. The thrown exception
+     * must be included as the cause property of this exception, if available. * @throws NullPointerException if context is
+     * <code>null</code>
+     */
+    public abstract boolean isReadOnly(ELContext context);
+
+    /**
+     * Evaluates the expression relative to the provided context, and returns the most general type that is acceptable for
+     * an object to be passed as the <code>value</code> parameter in a future call to the {@link #setValue} method.
+     *
+     * <p>
+     * This is not always the same as <code>getValue().getClass()</code>. For example, in the case of an expression that
+     * references an array element, the <code>getType</code> method will return the element type of the array, which might
+     * be a superclass of the type of the actual element that is currently in the specified array element.
+     *
+     * @param context The context of this evaluation.
+     * 
+     * @return the most general acceptable type; otherwise undefined.
+     * 
+     * @throws NullPointerException if context is <code>null</code>.
+     * @throws PropertyNotFoundException if one of the property resolutions failed because a specified variable or property
+     * does not exist or is not readable.
+     * @throws ELException if an exception was thrown while performing property or variable resolution. The thrown exception
+     * must be included as the cause property of this exception, if available.
+     */
+    public abstract Class<?> getType(ELContext context);
+
+    /**
+     * Returns the type the result of the expression will be coerced to after evaluation.
+     *
+     * @return the <code>expectedType</code> passed to the <code>ExpressionFactory.createValueExpression</code> method that
+     * created this <code>ValueExpression</code>.
+     */
+    public abstract Class<?> getExpectedType();
+
+    /**
+     * Returns a {@link ValueReference} for this expression instance.
+     *
+     * @param context the context of this evaluation
+     * 
+     * @return the <code>ValueReference</code> for this <code>ValueExpression</code>, or <code>null</code> if this
+     * <code>ValueExpression</code> is not a reference to a base (null or non-null) and a property. If the base is null, and
+     * the property is a Jakarta Expression Language variable, return the <code>ValueReference</code> for the 
+     * <code>ValueExpression</code> associated with this Jakarta Expression Language variable.
+     *
+     * @since Jakarta Expression Language 2.2
+     */
+    public ValueReference getValueReference(ELContext context) {
+        return null;
+    }
+}
diff --git a/api/src/main/java/javax/el/ValueReference.java b/api/src/main/java/javax/el/ValueReference.java
new file mode 100644
index 0000000..d486c7f
--- /dev/null
+++ b/api/src/main/java/javax/el/ValueReference.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2010, 2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package javax.el;
+
+import java.io.Serializable;
+
+/**
+ * This encapsulates a base model object and one of its properties.
+ *
+ * @since Jakarta Expression Language 2.2
+ */
+public class ValueReference implements Serializable {
+
+    private static final long serialVersionUID = -4076659531951367109L;
+
+    private Object base;
+    private Object property;
+
+    public ValueReference(Object base, Object property) {
+        this.base = base;
+        this.property = property;
+    }
+
+    public Object getBase() {
+        return base;
+    }
+
+    public Object getProperty() {
+        return property;
+    }
+
+}
diff --git a/api/src/main/java/javax/el/VariableMapper.java b/api/src/main/java/javax/el/VariableMapper.java
new file mode 100644
index 0000000..1b84214
--- /dev/null
+++ b/api/src/main/java/javax/el/VariableMapper.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 1997-2018 Oracle and/or its affiliates and others.
+ * All rights reserved.
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package javax.el;
+
+/**
+ * The interface to a map between Jakarta Expression Language variables and the Jakarta Expression Language expressions
+ * they are associated with.
+ *
+ * @since Jakarta Server Pages 2.1
+ */
+public abstract class VariableMapper {
+
+    /**
+     * @param variable The variable name
+     * 
+     * @return the ValueExpression assigned to the variable, null if there is no previous assignment to this variable.
+     */
+    public abstract ValueExpression resolveVariable(String variable);
+
+    /**
+     * Assign a ValueExpression to an Jakarta Expression Language variable, replacing any previously assignment to the same
+     * variable. The assignment for the variable is removed if the expression is <code>null</code>.
+     *
+     * @param variable The variable name
+     * @param expression The ValueExpression to be assigned to the variable.
+     * 
+     * @return The previous ValueExpression assigned to this variable, null if there is no previous assignment to this
+     * variable.
+     */
+    public abstract ValueExpression setVariable(String variable, ValueExpression expression);
+}
diff --git a/api/src/main/java/javax/el/package.html b/api/src/main/java/javax/el/package.html
new file mode 100644
index 0000000..22e6086
--- /dev/null
+++ b/api/src/main/java/javax/el/package.html
@@ -0,0 +1,239 @@
+<!--
+
+    Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved.
+    Copyright 2004 The Apache Software Foundation
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+</head>
+
+<body bgcolor="white">
+Provides the API for <strong>Jakarta Expression Language 3.0</strong>
+
+<p>Jakarta Expression Language is a simple language originally designed to
+satisfy the specific needs of web application developers. It has evolved
+into its own specification intended for general use inside and outside of the
+web containers.</p>
+
+<p>This package contains the classes and interfaces that describe
+and define the programmatic access to the Jakarta Expression Language engine. 
+The API is logically partitioned as follows:
+
+<ul>
+  <li><a href="#Context">Jakarta Expression Language Context</a></li>
+  <li><a href="#ExpressionObjects">Expression Objects</a></li>
+  <li><a href="#ExpressionCreation">Creation of Expressions</a></li>
+  <li><a href="#ExpressionEvaluation">Evaluation of Expressions</a></li>
+  <li><a href="#EvaluationListener">Evaluation Listeners</a></li>
+  <li><a href="#Resolver">Resolution of Model Objects and their Properties</a></li>
+  <li><a href="#Functions">Jakarta Expression Language Functions</a></li>
+  <li><a href="#Variables">Jakarta Expression Language Variables</a></li>
+  <li><a href="#Standalone">Jakarta Expression Language in Stand-alone environment</a></li>
+</ul>
+
+<h3><a name="Context">Jakarta Expression Language Context</a></h3>
+
+<p>An important goal of Jakarta Expression Language is to ensure it can be used in 
+a variety of environments. It must therefore provide enough flexibility
+to adapt to the specific requirements of the environment where it is
+being used.</p>
+
+<p>Class {@link javax.el.ELContext} is what links 
+the Jakarta Expression Language with the specific environment where it is being used.
+It provides the mechanism through which all relevant context for creating or
+evaluating an expression is specified.
+</p>
+
+<p>When Jakarta Expression Language is used in a web container, the creation of <code>ELContext
+</code> objects is controlled through the underlying technology.
+For example, in Jakarta Server Pages, the
+ <code>JspContext.getELContext()</code> factory method is used.  In an
+stand-alone environment, a default {@link javax.el.StandardELContext} is
+provided.</p>
+ 
+ <p>Some technologies provide the ability to add an {@link javax.el.ELContextListener}
+ so that applications and frameworks can ensure their own context objects
+ are attached to any newly created <code>ELContext</code>.</p>
+ 
+<h3><a name="ExpressionObjects">Expression Objects</a></h3>
+
+<p>At the core of the Expression Language is the notion of an <i>expression</i>
+that gets parsed according to the grammar defined by the Expression Language.</p>
+
+<p>There are two types of expressions defined by Jakarta Expression Language: <i>value expressions</i>
+and <i>method expressions</i>. A {@link javax.el.ValueExpression} such as 
+<code>"${customer.name}"</code> can be used either
+as an <i>rvalue</i> (return the value associated with property <code>name</code>
+of the model object <code>customer</code>) or as an <i>lvalue</i> 
+(set the value of the property <code>name</code> of the model object
+<code>customer</code>).</p>
+
+<p>A {@link javax.el.MethodExpression} such as 
+<code>"${handler.process}"</code> makes it possible to invoke a method 
+(<code>process</code>) on a specific model object (<code>handler</code>).</p>
+
+<p>In version 2.2 and later, either type of Jakarta Expression Language expression can represent a method
+invocation, such as <code>${trader.buy("JAVA")}</code>, where the arguments to
+the method invocation are specified in the expression.</p>
+
+<p>All expression classes extend the base class {@link javax.el.Expression}, making them
+serializable and forcing them to implement <code>equals()</code> and 
+<code>hashCode()</code>. Moreover, each method on these expression classes 
+that actually evaluates an expression receives a parameter
+of class {@link javax.el.ELContext},
+which provides the context required to evaluate the expression.</p>
+
+<h3><a name="ExpressionCreation">Creation of Expressions</a></h3>
+
+<p>An expression is created through the {@link javax.el.ExpressionFactory} class.
+The factory provides two creation methods; one for each type of expression
+ supported by Jakarta Expression Language.</p>
+
+<p>To create an expression, one must provide an {@link javax.el.ELContext}, 
+a string representing 
+the expression, and the expected type (<code>ValueExpression</code>) or signature 
+(<code>MethodExpression</code>). 
+
+The <code>ELContext</code> provides the context necessary to parse an expression.
+Specifically, if the expression uses a Jakarta Expression Language function 
+(for example <code>${fn:toUpperCase(customer.name)}</code>) or a Jakarta Expression Language 
+variable, then {@link javax.el.FunctionMapper} and {@link javax.el.VariableMapper}
+objects must be available within the <code>ELContext</code> so that Jakarta Expression Language 
+functions and Jakarta Expression Language variables are properly mapped.
+
+<h3><a name="ExpressionEvaluation">Evaluation of Expressions</a></h3>
+<p>The creation and the evaluation of an expression are done in two separate
+steps.  At the evaluation of an expression, the {@link javax.el.ELContext} 
+provides the context necessary to support property and method resolution
+for modal objects.</p>
+
+<p>A deferred expression is one that is created but not immediately evaluated.
+In a Jakarta Faces request processing life cycle, Jakarta Expression Language 
+expressions are typically created in the tree building phase and evaluated in the 
+rendering phrase.</p>
+
+<p>Adding parameters to a <code>ValueExpression</code> further enhances the
+power of deferred expressions.  The {@link javax.el.LambdaExpression}
+encapsulates such a construct.  A <code>LambdaExpression</code> can be
+invoked by supplying the actual parameters at evaluation.  It plays
+an important role in the support for collections operators.</p>
+
+<h3><a name="EvaluationListener">Evaluation Listeners</a></h3>
+<p>By registering {@link javax.el.EvaluationListener}s in ELContext, a user can
+receive notifications during the Jakarta Expression Language expression evaluations.  
+There are three events that trigger the notification:
+<ul>
+  <li>Before evaluation</li>
+  <li>After evaluation</li>
+  <li>When (base, property) is resolved</li>
+</ul></p>
+
+<h3><a name="Resolver">Resolution of Model Objects and their Properties</a></h3>
+
+<p>Through the {@link javax.el.ELResolver} base class, Jakarta Expression Language 
+features a pluggable mechanism to resolve model object references as well as properties 
+and method invocations of these objects.</p>
+
+<p>The Jakarta Expression Language API provides implementations of <code>ELResolver</code> supporting 
+property resolution for common data types which include arrays 
+({@link javax.el.ArrayELResolver}), JavaBeans ({@link javax.el.BeanELResolver}), <code>List</code>s ({@link javax.el.ListELResolver}), 
+<code>Map</code>s ({@link javax.el.MapELResolver}), and <code>ResourceBundle</code>s ({@link javax.el.ResourceBundleELResolver}).</p>
+
+<p>Tools can easily obtain more information about resolvable model objects and their 
+resolvable properties by calling
+method <code>getFeatureDescriptors</code> on the <code>ELResolver</code>. This method exposes objects
+of type <code>java.beans.FeatureDescriptor</code>, providing all information of interest 
+on top-level model objects as well as their properties.</p> 
+
+<h3><a name="Functions">Jakarta Expression Language Functions</a></h3>
+
+<p>If a Jakarta Expression Language expression uses a function 
+(for example <code>${fn:toUpperCase(customer.name)}</code>), then a 
+{@link javax.el.FunctionMapper} object must also be specified within 
+the <code>ELContext</code>. 
+
+The <code>FunctionMapper</code> is responsible to map
+ <code>${prefix:name()}</code> style functions to 
+static methods that can execute the specified functions. 
+</p>
+
+<h3><a name="Variables">Jakarta Expression Language Variables</a></h3>
+
+<p>Just like {@link javax.el.FunctionMapper} provides
+a flexible mechanism to add functions to Jakarta Expression Language, 
+{@link javax.el.VariableMapper} provides a flexible mechanism to support the notion of 
+<strong>Jakarta Expression Language variables</strong>.
+</p>
+ 
+<p>
+A Jakarta Expression Language variable does not directly refer to a model object that can then
+be resolved by an <code>ELResolver</code>. Instead, it refers to a Jakarta Expression Language
+expression. The evaluation of that Jakarta Expression Language expression gives the
+Jakarta Expression Language variable its value.
+</p>
+
+<p>
+For example, in the following code snippet
+<blockquote>
+  <code>&lt;h:inputText value="#{handler.customer.name}"/></code>
+</blockquote>
+
+<code>handler</code> refers to a model object that can be resolved by a Jakarta Expression Language Resolver.
+</p>
+<p>
+However, in this other example:
+<blockquote>
+<pre>
+&lt;c:forEach var="item" items="#{model.list}">
+   &lt;h:inputText value="#{item.name}"/>
+&lt;/c:forEach>
+</pre>
+</blockquote>
+
+<code>item</code> is a Jakarta Expression Language variable because it does not refer directly to a model
+object.  Instead, it refers to another Jakarta Expression Language expression, namely a
+specific item in the collection referred to by the Jakarta Expression Language expression
+<code>#{model.list}</code>.
+</p>
+
+<p>
+Assuming that there are three elements in <code>${model.list}</code>, this means
+that for each invocation of <code>&lt;h:inputText></code>, the following information 
+about <code>item</code> must be preserved in the {@link javax.el.VariableMapper}:
+<blockquote>
+    first invocation: <code>item</code> maps to first element in <code>${model.list}</code><br>
+    second invocation: <code>item</code> maps to second element in <code>${model.list}</code><br>
+    third invocation: <code>item</code> maps to third element in <code>${model.list}</code><br>
+</blockquote>
+<p>
+<code>VariableMapper</code> provides the mechanisms required to allow the mapping
+of a Jakarta Expression Language variable to the Jakarta Expression Language expression from which it gets its value.
+</p>
+
+<h3><a name="Standalone">Jakarta Expression Language in Stand-alone environment</a></h3>
+<p>Jakarta Expression Language 3.0 includes APIs for using Jakarta Expression Language in a stand-alone environment.</p>
+<p>{@link javax.el.ELProcessor} provides simple APIs for the direct
+evaluations of expressions. It also makes it easy to define functions,
+set variables, and define beans locally.</p>
+
+<p>{@link javax.el.ELManager} provides lower level APIs for managing the Jakarta Expression Language
+parsing and evaluation environment.  It contains a default ELContext {@link javax.el.StandardELContext}.</p>
+
+</body>
+</html>
+
diff --git a/api/src/main/javadoc/doc-files/speclicense.html b/api/src/main/javadoc/doc-files/speclicense.html
new file mode 100644
index 0000000..7c404e0
--- /dev/null
+++ b/api/src/main/javadoc/doc-files/speclicense.html
@@ -0,0 +1,72 @@
+<html>
+<head>
+<title>Eclipse Foundation Specification License - v1.0</title>
+</head>
+<body>
+<h1>Eclipse Foundation Specification License - v1.0</h1>
+<p>By using and/or copying this document, or the Eclipse Foundation
+  document from which this statement is linked, you (the licensee) agree
+  that you have read, understood, and will comply with the following
+  terms and conditions:</p>
+
+<p>Permission to copy, and distribute the contents of this document, or
+  the Eclipse Foundation document from which this statement is linked, in
+  any medium for any purpose and without fee or royalty is hereby
+  granted, provided that you include the following on ALL copies of the
+  document, or portions thereof, that you use:</p>
+
+<ul>
+  <li> link or URL to the original Eclipse Foundation document.</li>
+  <li>All existing copyright notices, or if one does not exist, a notice
+      (hypertext is preferred, but a textual representation is permitted)
+      of the form: &quot;Copyright &copy; [$date-of-document]
+      &ldquo;Eclipse Foundation, Inc. &lt;&lt;url to this license&gt;&gt;
+      &quot;
+  </li>
+</ul>
+
+<p>Inclusion of the full text of this NOTICE must be provided. We
+  request that authorship attribution be provided in any software,
+  documents, or other items or products that you create pursuant to the
+  implementation of the contents of this document, or any portion
+  thereof.</p>
+
+<p>No right to create modifications or derivatives of Eclipse Foundation
+  documents is granted pursuant to this license, except anyone may
+  prepare and distribute derivative works and portions of this document
+  in software that implements the specification, in supporting materials
+  accompanying such software, and in documentation of such software,
+  PROVIDED that all such works include the notice below. HOWEVER, the
+  publication of derivative works of this document for use as a technical
+  specification is expressly prohibited.</p>
+
+<p>The notice is:</p>
+
+<p>&quot;Copyright &copy; 2018 Eclipse Foundation. This software or
+  document includes material copied from or derived from [title and URI
+  of the Eclipse Foundation specification document].&quot;</p>
+
+<h2>Disclaimers</h2>
+
+<p>THIS DOCUMENT IS PROVIDED &quot;AS IS,&quot; AND THE COPYRIGHT
+  HOLDERS AND THE ECLIPSE FOUNDATION MAKE NO REPRESENTATIONS OR
+  WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+  WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
+  NON-INFRINGEMENT, OR TITLE; THAT THE CONTENTS OF THE DOCUMENT ARE
+  SUITABLE FOR ANY PURPOSE; NOR THAT THE IMPLEMENTATION OF SUCH CONTENTS
+  WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR
+  OTHER RIGHTS.</p>
+
+<p>THE COPYRIGHT HOLDERS AND THE ECLIPSE FOUNDATION WILL NOT BE LIABLE
+  FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT
+  OF ANY USE OF THE DOCUMENT OR THE PERFORMANCE OR IMPLEMENTATION OF THE
+  CONTENTS THEREOF.</p>
+
+<p>The name and trademarks of the copyright holders or the Eclipse
+  Foundation may NOT be used in advertising or publicity pertaining to
+  this document or its contents without specific, written prior
+  permission. Title to copyright in this document will at all times
+  remain with copyright holders.</p>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/exclude.xml b/exclude.xml
new file mode 100644
index 0000000..e25e681
--- /dev/null
+++ b/exclude.xml
@@ -0,0 +1,35 @@
+<!--
+
+    Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<FindBugsFilter>
+    <Match>
+        <Class name="com.sun.el.parser.SimpleCharStream"/>
+    </Match>
+    <Match>
+        <Class name="com.sun.el.parser.ELParser"/>
+    </Match>
+    <Match>
+        <Class name="com.sun.el.parser.ELParserTokenManager"/>
+    </Match>
+    <Match>
+        <Class name="com.sun.el.parser.ELParserConstants"/>
+    </Match>
+    <Match>
+        <Class name="com.sun.el.parser.TokenMgrError"/>
+    </Match>
+</FindBugsFilter>
diff --git a/impl/build.xml b/impl/build.xml
new file mode 100644
index 0000000..2ed734c
--- /dev/null
+++ b/impl/build.xml
@@ -0,0 +1,45 @@
+<!--
+
+    Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<!-- Use ant to generate .jj and .java files.  Use mvn for build. -->
+
+<project name="el-impl" default="generate" basedir=".">
+
+    <property name="javacc.home" value="/Users/kichung/tools/javacc-5.0"/>
+    <property name="dir" value="src/main/java/com/sun/el/parser"/>
+
+    <target name="generate" description="Generate java files">
+
+        <jjtree target="${dir}/ELParser.jjt"
+                outputdirectory="${dir}"
+                javacchome="${javacc.home}"/>
+        <javacc target="${dir}/ELParser.jj"
+                outputdirectory="${dir}"
+                javacchome="${javacc.home}"/>
+
+        <replaceregexp byline="true">
+          <regexp pattern="final private LookaheadSuccess"/>
+          <substitution expression="static final private LookaheadSuccess"/>
+          <fileset dir="src/main/java/com/sun/el/parser">
+            <include name="ELParser.java"/>
+          </fileset>
+        </replaceregexp>
+
+    </target>
+</project>
+
diff --git a/impl/pom.xml b/impl/pom.xml
new file mode 100644
index 0000000..9c28069
--- /dev/null
+++ b/impl/pom.xml
@@ -0,0 +1,276 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 1997, 2018 Oracle and/or its affiliates and others.
+    All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+	<modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.eclipse.ee4j</groupId>
+        <artifactId>project</artifactId>
+        <version>1.0.5</version>
+        <relativePath/>
+    </parent>
+    
+    <groupId>com.sun.el</groupId>
+    <artifactId>el-ri</artifactId>
+    <version>3.0.3-SNAPSHOT</version>
+
+    <name>Jakarta Expression Language 3.0 Implementation</name>
+    <description>Jakarta Expression Language Implementation</description>
+    <url>https://projects.eclipse.org/projects/ee4j.el</url>
+    
+    <properties>
+        <!-- the bundle build number must be the same as the maven number -->
+        <bundle.version>3.0.3</bundle.version>
+        <!-- The most current api version -->
+        <spec.version>3.0</spec.version>
+        <extensionName>javax.el.impl</extensionName>
+        <bundle.symbolicName>com.sun.el.javax.el</bundle.symbolicName>
+        <vendorName>Oracle Corporation</vendorName>
+        <findbugs.version>2.5.2</findbugs.version>
+        <findbugs.exclude>${project.basedir}/exclude.xml</findbugs.exclude>
+        <findbugs.threshold>High</findbugs.threshold>
+        <tlda-license.url>http://hudson-sca.us.oracle.com/job/tlda-license/lastSuccessfulBuild/artifact</tlda-license.url>
+    </properties>
+
+    <issueManagement>
+        <system>github</system>
+        <url>https://github.com/eclipse-ee4j/el-ri/issues</url>
+    </issueManagement>
+
+    <licenses>
+        <license>
+            <name>EPL 2.0</name>
+            <url>http://www.eclipse.org/legal/epl-2.0</url>
+            <distribution>repo</distribution>
+        </license>
+        <license>
+            <name>GPL2 w/ CPE</name>
+            <url>https://www.gnu.org/software/classpath/license.html</url>
+            <distribution>repo</distribution>
+        </license>
+    </licenses>
+    
+    <mailingLists>
+        <mailingList>
+            <name>Jakarta Expression Language 3.0 mailing list</name>
+            <post>el-dev@eclipse.org</post>
+            <subscribe>https://dev.eclipse.org/mailman/listinfo/el-dev</subscribe>
+            <unsubscribe>https://dev.eclipse.org/mailman/listinfo/el-dev</unsubscribe>
+            <archive>https://dev.eclipse.org/mhonarc/lists/el-dev</archive>
+        </mailingList>
+    </mailingLists>
+
+    <scm>
+        <connection>scm:git:https://github.com/eclipse-ee4j/el-ri.git</connection>
+        <developerConnection>
+            scm:git:git@github.com:eclipse-ee4j/el-ri.git
+        </developerConnection>
+        <url>https://github.com/eclipse-ee4j/el-ri</url>
+        <tag>HEAD</tag>
+    </scm>
+
+    <developers>
+        <developer>
+            <id>yaminikb</id>
+            <name>Yamini K B</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+        </developer>
+    </developers>
+
+    <contributors>
+        <contributor>
+            <name>Kin-man Chung</name>
+        </contributor>
+    </contributors>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>src/main/java</directory>
+                <includes>
+                    <include>**/*.properties</include>
+                    <include>**/*.xml</include>
+                </includes>
+            </resource>
+            <resource>
+                <directory>${project.basedir}/..</directory>
+                <includes>
+                    <include>LICENSE.md</include>
+                    <include>NOTICE.md</include>
+                </includes>
+                <targetPath>META-INF</targetPath>
+            </resource>
+        </resources>
+    
+    
+        <plugins>
+            <!-- Configure maven-bundle-plugin to generate OSGi manifest. Please note: we use the manifest goal only and not the bundle goal. 
+                The bundle goal can lead to very surprising results if the package names are not correctly specified. So, we use the jar plugin to generate the 
+                jar. -->
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <version>1.4.3</version>
+                <configuration>
+                    <instructions>
+                        <Bundle-SymbolicName>${bundle.symbolicName}</Bundle-SymbolicName>
+                        <Bundle-Description>
+                            Jakarta Expression Language ${spec.version} Implementation
+                        </Bundle-Description>
+                        <Bundle-Version>${bundle.version}</Bundle-Version>
+                        <Extension-Name>${extensionName}</Extension-Name>
+                        <Specification-Version>${spec.version}</Specification-Version>
+                        <Specification-Vendor>${vendorName}</Specification-Vendor>
+                        <Implementation-Version>${project.version}</Implementation-Version>
+                        <Implementation-Vendor>${vendorName}</Implementation-Vendor>
+                        <Export-Package>com.sun.el</Export-Package>
+                    </instructions>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>bundle-manifest</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>manifest</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            
+            <plugin>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>2.4</version>
+                <configuration>
+                    <archive>
+                        <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+                    </archive>
+                </configuration>
+            </plugin>
+            
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.0</version>
+                <configuration>
+                    <source>1.7</source>
+                    <target>1.7</target>
+                    <compilerArgument>-Xlint:unchecked</compilerArgument>
+                </configuration>
+            </plugin>
+
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>3.0.1</version>
+                <executions>
+                    <execution>
+                        <id>attach-javadocs</id>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                        <configuration>
+                            <additionalJOption>-Xdoclint:none</additionalJOption>
+                            <groups>
+                                <group>
+                                    <title>Jakarta Expression Language 3.0 Implementation</title>
+                                    <packages>com.sun.el</packages>
+                                </group>
+                            </groups>
+                            <bottom> Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved. Use is subject to license terms. </bottom>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            
+            <!-- Use ant to manually invoke javacc, as this required is very infrequently <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>javacc-maven-plugin</artifactId> 
+                <version>2.6</version> <executions> <execution> <id>jjtree-javacc</id> <goals> <goal>jjtree-javacc</goal> </goals> <configuration> <sourceDirectory>src/main/java/com/sun/el/parser</sourceDirectory> 
+                <outputDirectory>src/main/java/com/sun/el/parser</outputDirectory> </configuration> </execution> </executions> </plugin> -->
+            
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>findbugs-maven-plugin</artifactId>
+                <version>${findbugs.version}</version>
+                <configuration>
+                    <threshold>${findbugs.threshold}</threshold>
+                    <excludeFilterFile>${findbugs.exclude}</excludeFilterFile>
+                    <findbugsXmlOutput>true</findbugsXmlOutput>
+                    <findbugsXmlWithMessages>true</findbugsXmlWithMessages>
+                </configuration>
+            </plugin>
+            
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-release-plugin</artifactId>
+                <configuration>
+                    <mavenExecutorId>forked-path</mavenExecutorId>
+                    <useReleaseProfile>false</useReleaseProfile>
+                    <arguments>${release.arguments}</arguments>
+                </configuration>
+            </plugin>
+            
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>2.7.1</version>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.apache.maven.surefire</groupId>
+                        <artifactId>surefire-junit47</artifactId>
+                        <version>2.7.1</version>
+                    </dependency>
+                </dependencies>
+                <configuration>
+                    <forkMode>never</forkMode>
+                </configuration>
+            </plugin>
+	</plugins>
+    </build>
+
+    <reporting>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>findbugs-maven-plugin</artifactId>
+                <version>${findbugs.version}</version>
+                <configuration>
+                    <threshold>${findbugs.threshold}</threshold>
+                    <excludeFilterFile>${findbugs.exclude}</excludeFilterFile>
+                </configuration>
+            </plugin>
+        </plugins>
+    </reporting>
+
+    <dependencies>
+        <dependency>
+            <groupId>jakarta.el</groupId>
+            <artifactId>jakarta.el-api</artifactId>
+            <version>${bundle.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.12</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+   
+</project>
diff --git a/impl/src/main/java/com/sun/el/ExpressionFactoryImpl.java b/impl/src/main/java/com/sun/el/ExpressionFactoryImpl.java
new file mode 100644
index 0000000..ee9a042
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/ExpressionFactoryImpl.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.el.ELContext;
+import javax.el.ELException;
+import javax.el.ELResolver;
+import javax.el.ExpressionFactory;
+import javax.el.MethodExpression;
+import javax.el.ValueExpression;
+
+import com.sun.el.lang.ELSupport;
+import com.sun.el.lang.ExpressionBuilder;
+import com.sun.el.stream.StreamELResolver;
+import com.sun.el.util.MessageFactory;
+
+/**
+ * @see javax.el.ExpressionFactory
+ *
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @author Kin-man Chung
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public class ExpressionFactoryImpl extends ExpressionFactory {
+
+    private Properties properties;
+    private boolean isBackwardCompatible22;
+
+    public ExpressionFactoryImpl() {
+        super();
+    }
+
+    public ExpressionFactoryImpl(Properties properties) {
+        super();
+        this.properties = properties;
+        this.isBackwardCompatible22 = "true".equals(getProperty("javax.el.bc2.2"));
+    }
+
+    @Override
+    public Object coerceToType(Object obj, Class<?> type) {
+        try {
+            return ELSupport.coerceToType(obj, type, isBackwardCompatible22);
+        } catch (IllegalArgumentException ex) {
+            throw new ELException(ex);
+        }
+    }
+
+    @Override
+    public MethodExpression createMethodExpression(ELContext context, String expression, Class<?> expectedReturnType, Class<?>[] expectedParamTypes) {
+        MethodExpression methodExpression =
+                new ExpressionBuilder(expression, context)
+                    .createMethodExpression(expectedReturnType, expectedParamTypes);
+
+        if (expectedParamTypes == null && !methodExpression.isParametersProvided()) {
+            throw new NullPointerException(MessageFactory.get("error.method.nullParms"));
+        }
+
+        return methodExpression;
+    }
+
+    @Override
+    public ValueExpression createValueExpression(ELContext context, String expression, Class<?> expectedType) {
+        if (expectedType == null) {
+            throw new NullPointerException(MessageFactory.get("error.value.expectedType"));
+        }
+
+        return new ExpressionBuilder(expression, context).createValueExpression(expectedType);
+    }
+
+    @Override
+    public ValueExpression createValueExpression(Object instance, Class<?> expectedType) {
+        if (expectedType == null) {
+            throw new NullPointerException(MessageFactory.get("error.value.expectedType"));
+        }
+
+        return new ValueExpressionLiteral(instance, expectedType);
+    }
+
+    public String getProperty(String key) {
+        if (properties == null) {
+            return null;
+        }
+
+        return properties.getProperty(key);
+    }
+
+    @Override
+    public ELResolver getStreamELResolver() {
+        return new StreamELResolver();
+    }
+
+    @Override
+    public Map<String, Method> getInitFunctionMap() {
+        return new HashMap<String, Method>();
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/Messages.properties b/impl/src/main/java/com/sun/el/Messages.properties
new file mode 100644
index 0000000..802a38a
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/Messages.properties
@@ -0,0 +1,79 @@
+#
+# Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+#
+# This program and the accompanying materials are made available under the
+# terms of the Eclipse Public License v. 2.0, which is available at
+# http://www.eclipse.org/legal/epl-2.0.
+#
+# This Source Code may also be made available under the following Secondary
+# Licenses when the conditions for such availability set forth in the
+# Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+# version 2 with the GNU Classpath Exception, which is available at
+# https://www.gnu.org/software/classpath/license.html.
+#
+# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+#
+
+# General Errors
+error.convert=Cannot convert {0} of type {1} to {2}
+error.compare=Cannot compare {0} to {1}
+error.function=Problems calling function ''{0}''
+error.function.syntax=Syntax error in calling function ''{0}''
+error.unreachable.base=Target Unreachable, identifier ''{0}'' resolved to null
+error.unreachable.property=Target Unreachable, ''{0}'' returned null
+error.resolver.unhandled=ELResolver did not handle type: {0} with property of ''{1}''
+error.resolver.unhandled.null=ELResolver cannot handle a null base Object with identifier ''{0}''
+
+# ValueExpressionLiteral
+error.value.literal.write=ValueExpression is a literal and not writable: {0}
+
+# ExpressionFactoryImpl
+error.null=Expression cannot be null
+error.mixed=Expression cannot contain both '#{..}' and '${..}' : {0}
+error.method=Not a valid MethodExpression : {0}
+error.method.nullParms=Parameter types cannot be null
+error.value.expectedType=Expected type cannot be null
+
+# ExpressionMediator
+error.eval=Error Evaluating {0} : {1}
+
+# ValueSetVisitor
+error.syntax.set=Illegal Syntax for Set Operation
+
+error.syntax.assign=Illegal Syntax for Assign Operation
+
+# ReflectionUtil
+error.method.notfound=Method not found: {0}.{1}({2})
+error.method.ambiguous=Unable to find unambiguous method: {0}.{1}({2})
+error.property.notfound=Property ''{1}'' not found on {0}
+
+# ValidatingVisitor
+error.fnMapper.null=Expression uses functions, but no FunctionMapper was provided
+error.fnMapper.method=Function ''{0}'' not found
+error.fnMapper.paramcount=Function ''{0}'' specifies {1} params, but {2} were supplied
+
+# **ExpressionImpl
+error.context.null=ELContext was null
+
+# ArrayELResolver
+error.array.outofbounds=Index {0} is out of bounds for array of size {1}
+
+# ListELResolver
+error.list.outofbounds=Index {0} is out of bounds for list of size {1}
+
+# BeanELResolver
+error.property.notfound=Property ''{1}'' not found on type: {0}
+error.property.invocation=Property ''{1}'' threw an exception from type: {0}
+error.property.notreadable=Property ''{1}'' doesn't have a 'get' specified on type: {0}
+error.property.notwritable=Property ''{1}'' doesn't have a 'set' specified on type: {0}
+
+# AstValue
+error.method.name=An instance of {0} is specified as the static method name, it
+must be a String
+
+# AstType
+error.class.notfound=The specified class ''{0}'' not found
+
+# AstLambdaExpression
+error.lambda.call=A Lambda expression must return another Lambda expression in this syntax
+error.lambda.parameter.readonly=The Lambda parameter ''{0}'' is not writable
diff --git a/impl/src/main/java/com/sun/el/MethodExpressionImpl.java b/impl/src/main/java/com/sun/el/MethodExpressionImpl.java
new file mode 100644
index 0000000..fb0aea4
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/MethodExpressionImpl.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el;
+
+import static com.sun.el.util.ReflectionUtil.forName;
+import static com.sun.el.util.ReflectionUtil.toTypeArray;
+import static com.sun.el.util.ReflectionUtil.toTypeNameArray;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+import javax.el.ELContext;
+import javax.el.ELException;
+import javax.el.ELResolver;
+import javax.el.Expression;
+import javax.el.ExpressionFactory;
+import javax.el.FunctionMapper;
+import javax.el.MethodExpression;
+import javax.el.MethodInfo;
+import javax.el.MethodNotFoundException;
+import javax.el.PropertyNotFoundException;
+import javax.el.VariableMapper;
+
+import com.sun.el.lang.EvaluationContext;
+import com.sun.el.lang.ExpressionBuilder;
+import com.sun.el.parser.Node;
+
+/**
+ * An <code>Expression</code> that refers to a method on an object.
+ *
+ * <p>
+ * The {@link ExpressionFactory#createMethodExpression} method can be used to parse an expression string and return a
+ * concrete instance of <code>MethodExpression</code> that encapsulates the parsed expression. The
+ * {@link FunctionMapper} is used at parse time, not evaluation time, so one is not needed to evaluate an expression
+ * using this class. However, the {@link ELContext} is needed at evaluation time.
+ * </p>
+ *
+ * <p>
+ * The {@link #getMethodInfo} and {@link #invoke} methods will evaluate the expression each time they are called. The
+ * {@link ELResolver} in the <code>ELContext</code> is used to resolve the top-level variables and to determine the
+ * behavior of the <code>.</code> and <code>[]</code> operators. For any of the two methods, the
+ * {@link ELResolver#getValue} method is used to resolve all properties up to but excluding the last one. This provides
+ * the <code>base</code> object on which the method appears. If the <code>base</code> object is null, a
+ * <code>NullPointerException</code> must be thrown. At the last resolution, the final <code>property</code> is then
+ * coerced to a <code>String</code>, which provides the name of the method to be found. A method matching the name and
+ * expected parameters provided at parse time is found and it is either queried or invoked (depending on the method
+ * called on this <code>MethodExpression</code>).
+ * </p>
+ *
+ * <p>
+ * See the notes about comparison, serialization and immutability in the {@link Expression} javadocs.
+ *
+ * @see javax.el.ELResolver
+ * @see javax.el.Expression
+ * @see javax.el.ExpressionFactory
+ * @see javax.el.MethodExpression
+ *
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class MethodExpressionImpl extends MethodExpression implements Externalizable {
+
+    private Class<?> expectedType;
+    private String expr;
+    private FunctionMapper fnMapper;
+    private VariableMapper varMapper;
+    private Class<?>[] paramTypes;
+
+    private transient Node node;
+
+    public MethodExpressionImpl() {
+        super();
+    }
+
+    /**
+     * @param expr the expression
+     * @param node the node
+     * @param fnMapper the function mapper
+     * @param varMapper the variable mapper
+     * @param expectedType expected return type of method
+     * @param paramTypes the method parameters
+     */
+    public MethodExpressionImpl(String expr, Node node, FunctionMapper fnMapper, VariableMapper varMapper, Class<?> expectedType, Class<?>[] paramTypes) {
+        super();
+        this.expr = expr;
+        this.node = node;
+        this.fnMapper = fnMapper;
+        this.varMapper = varMapper;
+        this.expectedType = expectedType;
+        this.paramTypes = paramTypes;
+    }
+
+    /**
+     * Determines whether the specified object is equal to this <code>Expression</code>.
+     *
+     * <p>
+     * The result is <code>true</code> if and only if the argument is not <code>null</code>, is an <code>Expression</code>
+     * object that is the of the same type (<code>ValueExpression</code> or <code>MethodExpression</code>), and has an
+     * identical parsed representation.
+     * </p>
+     *
+     * <p>
+     * Note that two expressions can be equal if their expression Strings are different. For example,
+     * <code>${fn1:foo()}</code> and <code>${fn2:foo()}</code> are equal if their corresponding <code>FunctionMapper</code>s
+     * mapped <code>fn1:foo</code> and <code>fn2:foo</code> to the same method.
+     * </p>
+     *
+     * @param obj the <code>Object</code> to test for equality.
+     * @return <code>true</code> if <code>obj</code> equals this <code>Expression</code>; <code>false</code> otherwise.
+     * @see java.util.Hashtable
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof MethodExpressionImpl) {
+            MethodExpressionImpl methodExpressionImpl = (MethodExpressionImpl) obj;
+            return getNode().equals(methodExpressionImpl.getNode());
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns the original String used to create this <code>Expression</code>, unmodified.
+     *
+     * <p>
+     * This is used for debugging purposes but also for the purposes of comparison (e.g. to ensure the expression in a
+     * configuration file has not changed).
+     * </p>
+     *
+     * <p>
+     * This method does not provide sufficient information to re-create an expression. Two different expressions can have
+     * exactly the same expression string but different function mappings. Serialization should be used to save and restore
+     * the state of an <code>Expression</code>.
+     * </p>
+     *
+     * @return The original expression String.
+     *
+     * @see javax.el.Expression#getExpressionString()
+     */
+    @Override
+    public String getExpressionString() {
+        return expr;
+    }
+
+    /**
+     * Evaluates the expression relative to the provided context, and returns information about the actual referenced
+     * method.
+     *
+     * @param context The context of this evaluation
+     * @return an instance of <code>MethodInfo</code> containing information about the method the expression evaluated to.
+     * @throws NullPointerException if context is <code>null</code> or the base object is <code>null</code> on the last
+     * resolution.
+     * @throws PropertyNotFoundException if one of the property resolutions failed because a specified variable or property
+     * does not exist or is not readable.
+     * @throws MethodNotFoundException if no suitable method can be found.
+     * @throws ELException if an exception was thrown while performing property or variable resolution. The thrown exception
+     * must be included as the cause property of this exception, if available.
+     * @see javax.el.MethodExpression#getMethodInfo(javax.el.ELContext)
+     */
+    @Override
+    public MethodInfo getMethodInfo(ELContext context) throws PropertyNotFoundException, MethodNotFoundException, ELException {
+        return getNode().getMethodInfo(new EvaluationContext(context, fnMapper, varMapper), paramTypes);
+    }
+
+    /**
+     * @return The Node for the expression
+     * @throws ELException
+     */
+    private Node getNode() throws ELException {
+        if (node == null) {
+            node = ExpressionBuilder.createNode(expr);
+        }
+
+        return node;
+    }
+
+    /**
+     * Returns the hash code for this <code>Expression</code>.
+     *
+     * <p>
+     * See the note in the {@link #equals} method on how two expressions can be equal if their expression Strings are
+     * different. Recall that if two objects are equal according to the <code>equals(Object)</code> method, then calling the
+     * <code>hashCode</code> method on each of the two objects must produce the same integer result. Implementations must
+     * take special note and implement <code>hashCode</code> correctly.
+     * </p>
+     *
+     * @return The hash code for this <code>Expression</code>.
+     * @see #equals
+     * @see java.util.Hashtable
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getNode().hashCode();
+    }
+
+    /**
+     * Evaluates the expression relative to the provided context, invokes the method that was found using the supplied
+     * parameters, and returns the result of the method invocation.
+     *
+     * @param context The context of this evaluation.
+     * @param params The parameters to pass to the method, or <code>null</code> if no parameters.
+     * @return the result of the method invocation (<code>null</code> if the method has a <code>void</code> return type).
+     * @throws NullPointerException if context is <code>null</code> or the base object is <code>null</code> on the last
+     * resolution.
+     * @throws PropertyNotFoundException if one of the property resolutions failed because a specified variable or property
+     * does not exist or is not readable.
+     * @throws MethodNotFoundException if no suitable method can be found.
+     * @throws ELException if an exception was thrown while performing property or variable resolution. The thrown exception
+     * must be included as the cause property of this exception, if available. If the exception thrown is an
+     * <code>InvocationTargetException</code>, extract its <code>cause</code> and pass it to the <code>ELException</code>
+     * constructor.
+     * @see javax.el.MethodExpression#invoke(javax.el.ELContext, java.lang.Object[])
+     */
+    @Override
+    public Object invoke(ELContext context, Object[] params) throws PropertyNotFoundException, MethodNotFoundException, ELException {
+        EvaluationContext ctx = new EvaluationContext(context, fnMapper, varMapper);
+        ctx.notifyBeforeEvaluation(expr);
+
+        Object obj = getNode().invoke(ctx, paramTypes, params);
+
+        ctx.notifyAfterEvaluation(expr);
+        return obj;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.io.Externalizable#readExternal(java.io.ObjectInput)
+     */
+    @Override
+    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+        expr = in.readUTF();
+        String type = in.readUTF();
+
+        if (!"".equals(type)) {
+            expectedType = forName(type);
+        }
+
+        paramTypes = toTypeArray(((String[]) in.readObject()));
+        fnMapper = (FunctionMapper) in.readObject();
+        varMapper = (VariableMapper) in.readObject();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
+     */
+    @Override
+    public void writeExternal(ObjectOutput out) throws IOException {
+        out.writeUTF(expr);
+        out.writeUTF(expectedType != null ? expectedType.getName() : "");
+        out.writeObject(toTypeNameArray(paramTypes));
+        out.writeObject(fnMapper);
+        out.writeObject(varMapper);
+    }
+
+    @Override
+    public boolean isLiteralText() {
+        return false;
+    }
+
+    @Override
+    public boolean isParametersProvided() {
+        return getNode().isParametersProvided();
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/MethodExpressionLiteral.java b/impl/src/main/java/com/sun/el/MethodExpressionLiteral.java
new file mode 100644
index 0000000..ae2b5b6
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/MethodExpressionLiteral.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el;
+
+import static com.sun.el.util.ReflectionUtil.toTypeArray;
+import static com.sun.el.util.ReflectionUtil.toTypeNameArray;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+import javax.el.ELContext;
+import javax.el.ELException;
+import javax.el.MethodExpression;
+import javax.el.MethodInfo;
+
+import com.sun.el.util.ReflectionUtil;
+
+public class MethodExpressionLiteral extends MethodExpression implements Externalizable {
+
+    private Class<?> expectedType;
+    private String expr;
+    private Class<?>[] paramTypes;
+
+    public MethodExpressionLiteral() {
+        // do nothing
+    }
+
+    public MethodExpressionLiteral(String expr, Class<?> expectedType, Class<?>[] paramTypes) {
+        this.expr = expr;
+        this.expectedType = expectedType;
+        this.paramTypes = paramTypes;
+    }
+
+    @Override
+    public MethodInfo getMethodInfo(ELContext context) throws ELException {
+        return new MethodInfo(expr, expectedType, paramTypes);
+    }
+
+    @Override
+    public Object invoke(ELContext context, Object[] params) throws ELException {
+        if (expectedType == null) {
+            return expr;
+        }
+
+        try {
+            return context.convertToType(expr, expectedType);
+        } catch (Exception ex) {
+            throw new ELException(ex);
+        }
+    }
+
+    @Override
+    public String getExpressionString() {
+        return expr;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return obj instanceof MethodExpressionLiteral && this.hashCode() == obj.hashCode();
+    }
+
+    @Override
+    public int hashCode() {
+        return expr.hashCode();
+    }
+
+    @Override
+    public boolean isLiteralText() {
+        return true;
+    }
+
+    @Override
+    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+        expr = in.readUTF();
+        String type = in.readUTF();
+
+        if (!"".equals(type)) {
+            expectedType = ReflectionUtil.forName(type);
+        }
+
+        paramTypes = toTypeArray(((String[]) in.readObject()));
+    }
+
+    @Override
+    public void writeExternal(ObjectOutput out) throws IOException {
+        out.writeUTF(expr);
+        out.writeUTF(expectedType != null ? expectedType.getName() : "");
+        out.writeObject(toTypeNameArray(paramTypes));
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/ValueExpressionImpl.java b/impl/src/main/java/com/sun/el/ValueExpressionImpl.java
new file mode 100644
index 0000000..7f476eb
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/ValueExpressionImpl.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el;
+
+import static com.sun.el.util.ReflectionUtil.forName;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+import javax.el.ELContext;
+import javax.el.ELException;
+import javax.el.ELResolver;
+import javax.el.Expression;
+import javax.el.ExpressionFactory;
+import javax.el.FunctionMapper;
+import javax.el.PropertyNotFoundException;
+import javax.el.PropertyNotWritableException;
+import javax.el.ValueExpression;
+import javax.el.ValueReference;
+import javax.el.VariableMapper;
+
+import com.sun.el.lang.EvaluationContext;
+import com.sun.el.lang.ExpressionBuilder;
+import com.sun.el.parser.AstLiteralExpression;
+import com.sun.el.parser.Node;
+
+/**
+ * An <code>Expression</code> that can get or set a value.
+ *
+ * <p>
+ * In previous incarnations of this API, expressions could only be read. <code>ValueExpression</code> objects can now be
+ * used both to retrieve a value and to set a value. Expressions that can have a value set on them are referred to as
+ * l-value expressions. Those that cannot are referred to as r-value expressions. Not all r-value expressions can be
+ * used as l-value expressions (e.g. <code>"${1+1}"</code> or <code>"${firstName} ${lastName}"</code>). See the EL
+ * Specification for details. Expressions that cannot be used as l-values must always return <code>true</code> from
+ * <code>isReadOnly()</code>.
+ * </p>
+ *
+ * <p>
+ * The {@link ExpressionFactory#createValueExpression} method can be used to parse an expression string and return a
+ * concrete instance of <code>ValueExpression</code> that encapsulates the parsed expression. The {@link FunctionMapper}
+ * is used at parse time, not evaluation time, so one is not needed to evaluate an expression using this class. However,
+ * the {@link ELContext} is needed at evaluation time.
+ * </p>
+ *
+ * <p>
+ * The {@link #getValue}, {@link #setValue}, {@link #isReadOnly} and {@link #getType} methods will evaluate the
+ * expression each time they are called. The {@link ELResolver} in the <code>ELContext</code> is used to resolve the
+ * top-level variables and to determine the behavior of the <code>.</code> and <code>[]</code> operators. For any of the
+ * four methods, the {@link ELResolver#getValue} method is used to resolve all properties up to but excluding the last
+ * one. This provides the <code>base</code> object. At the last resolution, the <code>ValueExpression</code> will call
+ * the corresponding {@link ELResolver#getValue}, {@link ELResolver#setValue}, {@link ELResolver#isReadOnly} or
+ * {@link ELResolver#getType} method, depending on which was called on the <code>ValueExpression</code>.
+ * </p>
+ *
+ * <p>
+ * See the notes about comparison, serialization and immutability in the {@link Expression} javadocs.
+ *
+ * @see javax.el.ELResolver
+ * @see javax.el.Expression
+ * @see javax.el.ExpressionFactory
+ * @see javax.el.ValueExpression
+ *
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dochez $
+ */
+public final class ValueExpressionImpl extends ValueExpression implements Externalizable {
+
+    private Class<?> expectedType;
+    private String expr;
+    private FunctionMapper fnMapper;
+    private VariableMapper varMapper;
+    private transient Node node;
+
+    public ValueExpressionImpl() {
+
+    }
+
+    public ValueExpressionImpl(String expr, Node node, FunctionMapper fnMapper, VariableMapper varMapper, Class<?> expectedType) {
+        this.expr = expr;
+        this.node = node;
+        this.fnMapper = fnMapper;
+        this.varMapper = varMapper;
+        this.expectedType = expectedType;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof ValueExpressionImpl) {
+            ValueExpressionImpl valueExpressionImpl = (ValueExpressionImpl) obj;
+            return getNode().equals(valueExpressionImpl.getNode());
+        }
+
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see javax.el.ValueExpression#getExpectedType()
+     */
+    @Override
+    public Class<?> getExpectedType() {
+        return expectedType;
+    }
+
+    /**
+     * Returns the type the result of the expression will be coerced to after evaluation.
+     *
+     * @return the <code>expectedType</code> passed to the <code>ExpressionFactory.createValueExpression</code> method that
+     * created this <code>ValueExpression</code>.
+     *
+     * @see javax.el.Expression#getExpressionString()
+     */
+    @Override
+    public String getExpressionString() {
+        return expr;
+    }
+
+    /**
+     * @return The Node for the expression
+     * @throws ELException
+     */
+    private Node getNode() throws ELException {
+        if (node == null) {
+            node = ExpressionBuilder.createNode(expr);
+        }
+
+        return this.node;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see javax.el.ValueExpression#getType(javax.el.ELContext)
+     */
+    @Override
+    public Class<?> getType(ELContext context) throws PropertyNotFoundException, ELException {
+        return getNode().getType(new EvaluationContext(context, fnMapper, varMapper));
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see javax.el.ValueExpression#getValueReference(javax.el.ELContext)
+     */
+    @Override
+    public ValueReference getValueReference(ELContext context) throws PropertyNotFoundException, ELException {
+        return getNode().getValueReference(new EvaluationContext(context, fnMapper, varMapper));
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see javax.el.ValueExpression#getValue(javax.el.ELContext)
+     */
+    @Override
+    public Object getValue(ELContext context) throws PropertyNotFoundException, ELException {
+        EvaluationContext ctx = new EvaluationContext(context, fnMapper, varMapper);
+        ctx.notifyBeforeEvaluation(expr);
+
+        Object value = getNode().getValue(ctx);
+
+        if (expectedType != null) {
+            try {
+                value = ctx.convertToType(value, expectedType);
+            } catch (IllegalArgumentException ex) {
+                throw new ELException(ex);
+            }
+        }
+        ctx.notifyAfterEvaluation(expr);
+        return value;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        return getNode().hashCode();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see javax.el.ValueExpression#isLiteralText()
+     */
+    @Override
+    public boolean isLiteralText() {
+        try {
+            return getNode() instanceof AstLiteralExpression;
+        } catch (ELException ele) {
+            return false;
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see javax.el.ValueExpression#isReadOnly(javax.el.ELContext)
+     */
+    @Override
+    public boolean isReadOnly(ELContext context) throws PropertyNotFoundException, ELException {
+        return getNode().isReadOnly(new EvaluationContext(context, fnMapper, varMapper));
+    }
+
+    @Override
+    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+        expr = in.readUTF();
+        String type = in.readUTF();
+        if (!"".equals(type)) {
+            expectedType = forName(type);
+        }
+        fnMapper = (FunctionMapper) in.readObject();
+        varMapper = (VariableMapper) in.readObject();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see javax.el.ValueExpression#setValue(javax.el.ELContext, java.lang.Object)
+     */
+    @Override
+    public void setValue(ELContext context, Object value) throws PropertyNotFoundException, PropertyNotWritableException, ELException {
+        getNode().setValue(new EvaluationContext(context, fnMapper, varMapper), value);
+    }
+
+    @Override
+    public void writeExternal(ObjectOutput out) throws IOException {
+        out.writeUTF(expr);
+        out.writeUTF(expectedType != null ? expectedType.getName() : "");
+        out.writeObject(fnMapper);
+        out.writeObject(varMapper);
+    }
+
+    @Override
+    public String toString() {
+        return "ValueExpression[" + expr + "]";
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/ValueExpressionLiteral.java b/impl/src/main/java/com/sun/el/ValueExpressionLiteral.java
new file mode 100644
index 0000000..7dc21e9
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/ValueExpressionLiteral.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el;
+
+import static com.sun.el.util.ReflectionUtil.forName;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+import javax.el.ELContext;
+import javax.el.ELException;
+import javax.el.PropertyNotWritableException;
+import javax.el.ValueExpression;
+
+import com.sun.el.util.MessageFactory;
+
+public final class ValueExpressionLiteral extends ValueExpression implements Externalizable {
+
+    private static final long serialVersionUID = 1L;
+
+    private Object value;
+    private Class<?> expectedType;
+
+    public ValueExpressionLiteral() {
+        super();
+    }
+
+    public ValueExpressionLiteral(Object value, Class<?> expectedType) {
+        this.value = value;
+        this.expectedType = expectedType;
+    }
+
+    @Override
+    public Object getValue(ELContext context) {
+        if (expectedType != null) {
+            try {
+                return context.convertToType(value, expectedType);
+            } catch (IllegalArgumentException ex) {
+                throw new ELException(ex);
+            }
+        }
+
+        return value;
+    }
+
+    @Override
+    public void setValue(ELContext context, Object value) {
+        throw new PropertyNotWritableException(MessageFactory.get("error.value.literal.write", value));
+    }
+
+    @Override
+    public boolean isReadOnly(ELContext context) {
+        return true;
+    }
+
+    @Override
+    public Class<?> getType(ELContext context) {
+        return value != null ? value.getClass() : null;
+    }
+
+    @Override
+    public Class<?> getExpectedType() {
+        return expectedType;
+    }
+
+    @Override
+    public String getExpressionString() {
+        return value != null ? value.toString() : null;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return obj instanceof ValueExpressionLiteral && this.equals((ValueExpressionLiteral) obj);
+    }
+
+    public boolean equals(ValueExpressionLiteral ve) {
+        return (ve != null && (this.value != null && ve.value != null && (this.value == ve.value || this.value.equals(ve.value))));
+    }
+
+    @Override
+    public int hashCode() {
+        return value != null ? value.hashCode() : 0;
+    }
+
+    @Override
+    public boolean isLiteralText() {
+        return true;
+    }
+
+    @Override
+    public void writeExternal(ObjectOutput out) throws IOException {
+        out.writeObject(value);
+        out.writeUTF(expectedType != null ? expectedType.getName() : "");
+    }
+
+    @Override
+    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+        value = in.readObject();
+        String type = in.readUTF();
+        if (!"".equals(type)) {
+            expectedType = forName(type);
+        }
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/lang/ELArithmetic.java b/impl/src/main/java/com/sun/el/lang/ELArithmetic.java
new file mode 100644
index 0000000..37092d4
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/lang/ELArithmetic.java
@@ -0,0 +1,423 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.lang;
+
+import static java.math.BigDecimal.ROUND_HALF_UP;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.sun.el.util.MessageFactory;
+
+/**
+ * A helper class of Arithmetic defined by the Jakarta Expression Specification
+ *
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public abstract class ELArithmetic {
+
+    public final static class BigDecimalDelegate extends ELArithmetic {
+
+        @Override
+        protected Number add(Number num0, Number num1) {
+            return ((BigDecimal) num0).add((BigDecimal) num1);
+        }
+
+        @Override
+        protected Number coerce(Number num) {
+            if (num instanceof BigDecimal) {
+                return num;
+            }
+            if (num instanceof BigInteger) {
+                return new BigDecimal((BigInteger) num);
+            }
+
+            return new BigDecimal(num.doubleValue());
+        }
+
+        @Override
+        protected Number coerce(String str) {
+            return new BigDecimal(str);
+        }
+
+        @Override
+        protected Number divide(Number num0, Number num1) {
+            return ((BigDecimal) num0).divide((BigDecimal) num1, ROUND_HALF_UP);
+        }
+
+        @Override
+        protected Number subtract(Number num0, Number num1) {
+            return ((BigDecimal) num0).subtract((BigDecimal) num1);
+        }
+
+        @Override
+        protected Number mod(Number num0, Number num1) {
+            return Double.valueOf(num0.doubleValue() % num1.doubleValue());
+        }
+
+        @Override
+        protected Number multiply(Number num0, Number num1) {
+            return ((BigDecimal) num0).multiply((BigDecimal) num1);
+        }
+
+        @Override
+        public boolean matches(Object obj0, Object obj1) {
+            return (obj0 instanceof BigDecimal || obj1 instanceof BigDecimal);
+        }
+    }
+
+    public final static class BigIntegerDelegate extends ELArithmetic {
+
+        @Override
+        protected Number add(Number num0, Number num1) {
+            return ((BigInteger) num0).add((BigInteger) num1);
+        }
+
+        @Override
+        protected Number coerce(Number num) {
+            if (num instanceof BigInteger) {
+                return num;
+            }
+
+            return new BigInteger(num.toString());
+        }
+
+        @Override
+        protected Number coerce(String str) {
+            return new BigInteger(str);
+        }
+
+        @Override
+        protected Number divide(Number num0, Number num1) {
+            return (new BigDecimal((BigInteger) num0)).divide(new BigDecimal((BigInteger) num1), ROUND_HALF_UP);
+        }
+
+        @Override
+        protected Number multiply(Number num0, Number num1) {
+            return ((BigInteger) num0).multiply((BigInteger) num1);
+        }
+
+        @Override
+        protected Number mod(Number num0, Number num1) {
+            return ((BigInteger) num0).mod((BigInteger) num1);
+        }
+
+        @Override
+        protected Number subtract(Number num0, Number num1) {
+            return ((BigInteger) num0).subtract((BigInteger) num1);
+        }
+
+        @Override
+        public boolean matches(Object obj0, Object obj1) {
+            return obj0 instanceof BigInteger || obj1 instanceof BigInteger;
+        }
+    }
+
+    public final static class DoubleDelegate extends ELArithmetic {
+
+        @Override
+        protected Number add(Number num0, Number num1) {
+            // could only be one of these
+            if (num0 instanceof BigDecimal) {
+                return ((BigDecimal) num0).add(new BigDecimal(num1.doubleValue()));
+            }
+            if (num1 instanceof BigDecimal) {
+                return ((new BigDecimal(num0.doubleValue()).add((BigDecimal) num1)));
+            }
+
+            return Double.valueOf(num0.doubleValue() + num1.doubleValue());
+        }
+
+        @Override
+        protected Number coerce(Number num) {
+            if (num instanceof Double) {
+                return num;
+            }
+            if (num instanceof BigInteger) {
+                return new BigDecimal((BigInteger) num);
+            }
+
+            return Double.valueOf(num.doubleValue());
+        }
+
+        @Override
+        protected Number coerce(String str) {
+            return Double.valueOf(str);
+        }
+
+        @Override
+        protected Number divide(Number num0, Number num1) {
+            return Double.valueOf(num0.doubleValue() / num1.doubleValue());
+        }
+
+        @Override
+        protected Number mod(Number num0, Number num1) {
+            return Double.valueOf(num0.doubleValue() % num1.doubleValue());
+        }
+
+        @Override
+        protected Number subtract(Number num0, Number num1) {
+            // could only be one of these
+            if (num0 instanceof BigDecimal) {
+                return ((BigDecimal) num0).subtract(new BigDecimal(num1.doubleValue()));
+            }
+
+            if (num1 instanceof BigDecimal) {
+                return ((new BigDecimal(num0.doubleValue()).subtract((BigDecimal) num1)));
+            }
+
+            return Double.valueOf(num0.doubleValue() - num1.doubleValue());
+        }
+
+        @Override
+        protected Number multiply(Number num0, Number num1) {
+            // could only be one of these
+            if (num0 instanceof BigDecimal) {
+                return ((BigDecimal) num0).multiply(new BigDecimal(num1.doubleValue()));
+            }
+            if (num1 instanceof BigDecimal) {
+                return ((new BigDecimal(num0.doubleValue()).multiply((BigDecimal) num1)));
+            }
+
+            return Double.valueOf(num0.doubleValue() * num1.doubleValue());
+        }
+
+        @Override
+        public boolean matches(Object obj0, Object obj1) {
+            return (obj0 instanceof Double || obj1 instanceof Double || obj0 instanceof Float || obj1 instanceof Float
+                    || (obj0 != null && (Double.TYPE == obj0.getClass() || Float.TYPE == obj0.getClass()))
+                    || (obj1 != null && (Double.TYPE == obj1.getClass() || Float.TYPE == obj1.getClass()))
+                    || (obj0 instanceof String && ELSupport.isStringFloat((String) obj0))
+                    || (obj1 instanceof String && ELSupport.isStringFloat((String) obj1)));
+        }
+    }
+
+    public final static class LongDelegate extends ELArithmetic {
+
+        @Override
+        protected Number add(Number num0, Number num1) {
+            return Long.valueOf(num0.longValue() + num1.longValue());
+        }
+
+        @Override
+        protected Number coerce(Number num) {
+            if (num instanceof Long) {
+                return num;
+            }
+
+            return Long.valueOf(num.longValue());
+        }
+
+        @Override
+        protected Number coerce(String str) {
+            return Long.valueOf(str);
+        }
+
+        @Override
+        protected Number divide(Number num0, Number num1) {
+            return Long.valueOf(num0.longValue() / num1.longValue());
+        }
+
+        @Override
+        protected Number mod(Number num0, Number num1) {
+            return Long.valueOf(num0.longValue() % num1.longValue());
+        }
+
+        @Override
+        protected Number subtract(Number num0, Number num1) {
+            return Long.valueOf(num0.longValue() - num1.longValue());
+        }
+
+        @Override
+        protected Number multiply(Number num0, Number num1) {
+            return Long.valueOf(num0.longValue() * num1.longValue());
+        }
+
+        @Override
+        public boolean matches(Object obj0, Object obj1) {
+            return obj0 instanceof Long || obj1 instanceof Long;
+        }
+    }
+
+    public static final BigDecimalDelegate BIGDECIMAL = new BigDecimalDelegate();
+    public static final BigIntegerDelegate BIGINTEGER = new BigIntegerDelegate();
+    public static final DoubleDelegate DOUBLE = new DoubleDelegate();
+    public static final LongDelegate LONG = new LongDelegate();
+    private static final Long ZERO = Long.valueOf(0);
+
+    public final static Number add(final Object obj0, final Object obj1) {
+        if (obj0 == null && obj1 == null) {
+            return Long.valueOf(0);
+        }
+
+        final ELArithmetic delegate;
+        if (BIGDECIMAL.matches(obj0, obj1)) {
+            delegate = BIGDECIMAL;
+        } else if (DOUBLE.matches(obj0, obj1)) {
+            delegate = DOUBLE;
+        } else if (BIGINTEGER.matches(obj0, obj1)) {
+            delegate = BIGINTEGER;
+        } else {
+            delegate = LONG;
+        }
+
+        Number num0 = delegate.coerce(obj0);
+        Number num1 = delegate.coerce(obj1);
+
+        return delegate.add(num0, num1);
+    }
+
+    public final static Number mod(final Object obj0, final Object obj1) {
+        if (obj0 == null && obj1 == null) {
+            return Long.valueOf(0);
+        }
+
+        final ELArithmetic delegate;
+        if (BIGDECIMAL.matches(obj0, obj1)) {
+            delegate = BIGDECIMAL;
+        } else if (DOUBLE.matches(obj0, obj1)) {
+            delegate = DOUBLE;
+        } else if (BIGINTEGER.matches(obj0, obj1)) {
+            delegate = BIGINTEGER;
+        } else {
+            delegate = LONG;
+        }
+
+        Number num0 = delegate.coerce(obj0);
+        Number num1 = delegate.coerce(obj1);
+
+        return delegate.mod(num0, num1);
+    }
+
+    public final static Number subtract(final Object obj0, final Object obj1) {
+        if (obj0 == null && obj1 == null) {
+            return Long.valueOf(0);
+        }
+
+        final ELArithmetic delegate;
+        if (BIGDECIMAL.matches(obj0, obj1)) {
+            delegate = BIGDECIMAL;
+        } else if (DOUBLE.matches(obj0, obj1)) {
+            delegate = DOUBLE;
+        } else if (BIGINTEGER.matches(obj0, obj1)) {
+            delegate = BIGINTEGER;
+        } else {
+            delegate = LONG;
+        }
+
+        Number num0 = delegate.coerce(obj0);
+        Number num1 = delegate.coerce(obj1);
+
+        return delegate.subtract(num0, num1);
+    }
+
+    public final static Number divide(final Object obj0, final Object obj1) {
+        if (obj0 == null && obj1 == null) {
+            return ZERO;
+        }
+
+        final ELArithmetic delegate;
+        if (BIGDECIMAL.matches(obj0, obj1)) {
+            delegate = BIGDECIMAL;
+        } else if (BIGINTEGER.matches(obj0, obj1)) {
+            delegate = BIGDECIMAL;
+        } else {
+            delegate = DOUBLE;
+        }
+
+        Number num0 = delegate.coerce(obj0);
+        Number num1 = delegate.coerce(obj1);
+
+        return delegate.divide(num0, num1);
+    }
+
+    public final static Number multiply(final Object obj0, final Object obj1) {
+        if (obj0 == null && obj1 == null) {
+            return Long.valueOf(0);
+        }
+
+        final ELArithmetic delegate;
+        if (BIGDECIMAL.matches(obj0, obj1)) {
+            delegate = BIGDECIMAL;
+        } else if (DOUBLE.matches(obj0, obj1)) {
+            delegate = DOUBLE;
+        } else if (BIGINTEGER.matches(obj0, obj1)) {
+            delegate = BIGINTEGER;
+        } else {
+            delegate = LONG;
+        }
+
+        Number num0 = delegate.coerce(obj0);
+        Number num1 = delegate.coerce(obj1);
+
+        return delegate.multiply(num0, num1);
+    }
+
+    public final static boolean isNumber(final Object obj) {
+        return (obj != null && isNumberType(obj.getClass()));
+    }
+
+    public final static boolean isNumberType(final Class type) {
+        return type == Long.TYPE || type == Double.TYPE || type == Byte.TYPE || type == Short.TYPE || type == Integer.TYPE || type == Float.TYPE
+                || Number.class.isAssignableFrom(type);
+    }
+
+    /**
+     *
+     */
+    protected ELArithmetic() {
+        super();
+    }
+
+    protected abstract Number add(final Number num0, final Number num1);
+
+    protected abstract Number multiply(final Number num0, final Number num1);
+
+    protected abstract Number subtract(final Number num0, final Number num1);
+
+    protected abstract Number mod(final Number num0, final Number num1);
+
+    protected abstract Number coerce(final Number num);
+
+    protected final Number coerce(final Object obj) {
+
+        if (isNumber(obj)) {
+            return coerce((Number) obj);
+        }
+        if (obj instanceof String) {
+            return coerce((String) obj);
+        }
+        if (obj == null || "".equals(obj)) {
+            return coerce(ZERO);
+        }
+
+        Class objType = obj.getClass();
+        if (Character.class.equals(objType) || Character.TYPE == objType) {
+            return coerce(Short.valueOf((short) ((Character) obj).charValue()));
+        }
+
+        throw new IllegalArgumentException(MessageFactory.get("el.convert", obj, objType));
+    }
+
+    protected abstract Number coerce(final String str);
+
+    protected abstract Number divide(final Number num0, final Number num1);
+
+    protected abstract boolean matches(final Object obj0, final Object obj1);
+
+}
diff --git a/impl/src/main/java/com/sun/el/lang/ELSupport.java b/impl/src/main/java/com/sun/el/lang/ELSupport.java
new file mode 100644
index 0000000..f0a8368
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/lang/ELSupport.java
@@ -0,0 +1,467 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.lang;
+
+import java.beans.PropertyEditor;
+import java.beans.PropertyEditorManager;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import javax.el.ELException;
+import javax.el.PropertyNotFoundException;
+
+import com.sun.el.util.MessageFactory;
+
+/**
+ * A helper class that implements the Jakarta Expression Specification
+ *
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @author Kin-man Chung
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public class ELSupport {
+
+    private final static Long ZERO = Long.valueOf(0L);
+
+    public final static void throwUnhandled(Object base, Object property) throws ELException {
+        if (base == null) {
+            throw new PropertyNotFoundException(MessageFactory.get("error.resolver.unhandled.null", property));
+        } else {
+            throw new PropertyNotFoundException(MessageFactory.get("error.resolver.unhandled", base.getClass(), property));
+        }
+    }
+
+    /**
+     * @param obj0 First object to be compared
+     * @param obj1 Second object to be compared
+     * @return The result (an int with values -1, 0, or 1) of the comparison
+     * @throws ELException when something goes wrong
+     */
+    public final static int compare(final Object obj0, final Object obj1) throws ELException {
+        if (obj0 == obj1 || equals(obj0, obj1)) {
+            return 0;
+        }
+        if (isBigDecimalOp(obj0, obj1)) {
+            BigDecimal bd0 = (BigDecimal) coerceToNumber(obj0, BigDecimal.class);
+            BigDecimal bd1 = (BigDecimal) coerceToNumber(obj1, BigDecimal.class);
+            return bd0.compareTo(bd1);
+        }
+        if (isDoubleOp(obj0, obj1)) {
+            Double d0 = (Double) coerceToNumber(obj0, Double.class);
+            Double d1 = (Double) coerceToNumber(obj1, Double.class);
+            return d0.compareTo(d1);
+        }
+        if (isBigIntegerOp(obj0, obj1)) {
+            BigInteger bi0 = (BigInteger) coerceToNumber(obj0, BigInteger.class);
+            BigInteger bi1 = (BigInteger) coerceToNumber(obj1, BigInteger.class);
+            return bi0.compareTo(bi1);
+        }
+        if (isLongOp(obj0, obj1)) {
+            Long l0 = (Long) coerceToNumber(obj0, Long.class);
+            Long l1 = (Long) coerceToNumber(obj1, Long.class);
+            return l0.compareTo(l1);
+        }
+        if (obj0 instanceof String || obj1 instanceof String) {
+            return coerceToString(obj0).compareTo(coerceToString(obj1));
+        }
+        if (obj0 instanceof Comparable) {
+            // Safe cast
+            @SuppressWarnings("unchecked")
+            Comparable<Object> cobj0 = (Comparable) obj0;
+            return (obj1 != null) ? cobj0.compareTo(obj1) : 1;
+        }
+        if (obj1 instanceof Comparable) {
+            // Safe cast
+            @SuppressWarnings("unchecked")
+            Comparable<Object> cobj1 = (Comparable) obj1;
+            return (obj0 != null) ? -(cobj1.compareTo(obj0)) : -1;
+        }
+        throw new ELException(MessageFactory.get("error.compare", obj0, obj1));
+    }
+
+    /**
+     * @param obj0 Fisrt object to be compared
+     * @param obj1 Second object to be compared
+     * @return true if the objects compared equal
+     * @throws ELException when something goes wrong
+     */
+    public final static boolean equals(final Object obj0, final Object obj1) throws ELException {
+        if (obj0 == obj1) {
+            return true;
+        }
+        if (obj0 == null || obj1 == null) {
+            return false;
+        }
+        if (obj0 instanceof Boolean || obj1 instanceof Boolean) {
+            return coerceToBoolean(obj0).equals(coerceToBoolean(obj1));
+        }
+        if (obj0.getClass().isEnum()) {
+            return obj0.equals(coerceToEnum(obj1, obj0.getClass()));
+        }
+        if (obj1.getClass().isEnum()) {
+            return obj1.equals(coerceToEnum(obj0, obj1.getClass()));
+        }
+        if (obj0 instanceof String || obj1 instanceof String) {
+            return coerceToString(obj0).equals(coerceToString(obj1));
+        }
+        if (isBigDecimalOp(obj0, obj1)) {
+            BigDecimal bd0 = (BigDecimal) coerceToNumber(obj0, BigDecimal.class);
+            BigDecimal bd1 = (BigDecimal) coerceToNumber(obj1, BigDecimal.class);
+            return bd0.equals(bd1);
+        }
+        if (isDoubleOp(obj0, obj1)) {
+            Double d0 = (Double) coerceToNumber(obj0, Double.class);
+            Double d1 = (Double) coerceToNumber(obj1, Double.class);
+            return d0.equals(d1);
+        }
+        if (isBigIntegerOp(obj0, obj1)) {
+            BigInteger bi0 = (BigInteger) coerceToNumber(obj0, BigInteger.class);
+            BigInteger bi1 = (BigInteger) coerceToNumber(obj1, BigInteger.class);
+            return bi0.equals(bi1);
+        }
+        if (isLongOp(obj0, obj1)) {
+            Long l0 = (Long) coerceToNumber(obj0, Long.class);
+            Long l1 = (Long) coerceToNumber(obj1, Long.class);
+            return l0.equals(l1);
+        } else {
+            return obj0.equals(obj1);
+        }
+    }
+
+    /**
+     * @param obj Object to be coerced
+     * @return The result of coercion
+     */
+    public final static Boolean coerceToBoolean(final Object obj) throws IllegalArgumentException {
+        if (obj == null || "".equals(obj)) {
+            return Boolean.FALSE;
+        }
+        if (obj instanceof Boolean) {
+            return (Boolean) obj;
+        }
+        if (obj instanceof String) {
+            return Boolean.valueOf((String) obj);
+        }
+
+        throw new IllegalArgumentException(MessageFactory.get("error.convert", obj, obj.getClass(), Boolean.class));
+    }
+
+    // Enum types are hard construct. We can declare this as
+    // <T extends Enum<T>> T coerceToEnum(Object, Class<T> type)
+    // but this makes it harder to get the calls right.
+    @SuppressWarnings("unchecked")
+    public final static Enum coerceToEnum(final Object obj, Class type) {
+        if (obj == null || "".equals(obj)) {
+            return null;
+        }
+        if (obj.getClass().isEnum()) {
+            return (Enum) obj;
+        }
+        return Enum.valueOf(type, obj.toString());
+    }
+
+    public final static Character coerceToCharacter(final Object obj) throws IllegalArgumentException {
+        if (obj == null || "".equals(obj)) {
+            return Character.valueOf((char) 0);
+        }
+        if (obj instanceof String) {
+            return Character.valueOf(((String) obj).charAt(0));
+        }
+        if (ELArithmetic.isNumber(obj)) {
+            return Character.valueOf((char) ((Number) obj).shortValue());
+        }
+        Class objType = obj.getClass();
+        if (obj instanceof Character) {
+            return (Character) obj;
+        }
+
+        throw new IllegalArgumentException(MessageFactory.get("error.convert", obj, objType, Character.class));
+    }
+
+    public final static Number coerceToNumber(final Object obj) {
+        if (obj == null) {
+            return ZERO;
+        } else if (obj instanceof Number) {
+            return (Number) obj;
+        } else {
+            String str = coerceToString(obj);
+            if (isStringFloat(str)) {
+                return toFloat(str);
+            } else {
+                return toNumber(str);
+            }
+        }
+    }
+
+    protected final static Number coerceToNumber(final Number number, final Class type) throws IllegalArgumentException {
+        if (Long.TYPE == type || Long.class.equals(type)) {
+            return Long.valueOf(number.longValue());
+        }
+        if (Double.TYPE == type || Double.class.equals(type)) {
+            return Double.valueOf(number.doubleValue());
+        }
+        if (Integer.TYPE == type || Integer.class.equals(type)) {
+            return Integer.valueOf(number.intValue());
+        }
+        if (BigInteger.class.equals(type)) {
+            if (number instanceof BigDecimal) {
+                return ((BigDecimal) number).toBigInteger();
+            }
+            if (number instanceof BigInteger) {
+                return number;
+            }
+            return BigInteger.valueOf(number.longValue());
+        }
+        if (BigDecimal.class.equals(type)) {
+            if (number instanceof BigDecimal) {
+                return number;
+            }
+            if (number instanceof BigInteger) {
+                return new BigDecimal((BigInteger) number);
+            }
+            if (number instanceof Long) {
+                return new BigDecimal((Long) number);
+            }
+            return new BigDecimal(number.doubleValue());
+        }
+        if (Byte.TYPE == type || Byte.class.equals(type)) {
+            return Byte.valueOf(number.byteValue());
+        }
+        if (Short.TYPE == type || Short.class.equals(type)) {
+            return Short.valueOf(number.shortValue());
+        }
+        if (Float.TYPE == type || Float.class.equals(type)) {
+            return Float.valueOf(number.floatValue());
+        }
+
+        throw new IllegalArgumentException(MessageFactory.get("error.convert", number, number.getClass(), type));
+    }
+
+    public final static Number coerceToNumber(final Object obj, final Class type) throws IllegalArgumentException {
+        if (obj == null || "".equals(obj)) {
+            return coerceToNumber(ZERO, type);
+        }
+        if (obj instanceof String) {
+            return coerceToNumber((String) obj, type);
+        }
+        if (ELArithmetic.isNumber(obj)) {
+            if (obj.getClass().equals(type)) {
+                return (Number) obj;
+            }
+            return coerceToNumber((Number) obj, type);
+        }
+
+        if (obj instanceof Character) {
+            return coerceToNumber(Short.valueOf((short) ((Character) obj).charValue()), type);
+        }
+
+        throw new IllegalArgumentException(MessageFactory.get("error.convert", obj, obj.getClass(), type));
+    }
+
+    protected final static Number coerceToNumber(final String val, final Class type) throws IllegalArgumentException {
+        if (Long.TYPE == type || Long.class.equals(type)) {
+            return Long.valueOf(val);
+        }
+        if (Integer.TYPE == type || Integer.class.equals(type)) {
+            return Integer.valueOf(val);
+        }
+        if (Double.TYPE == type || Double.class.equals(type)) {
+            return Double.valueOf(val);
+        }
+        if (BigInteger.class.equals(type)) {
+            return new BigInteger(val);
+        }
+        if (BigDecimal.class.equals(type)) {
+            return new BigDecimal(val);
+        }
+        if (Byte.TYPE == type || Byte.class.equals(type)) {
+            return Byte.valueOf(val);
+        }
+        if (Short.TYPE == type || Short.class.equals(type)) {
+            return Short.valueOf(val);
+        }
+        if (Float.TYPE == type || Float.class.equals(type)) {
+            return Float.valueOf(val);
+        }
+
+        throw new IllegalArgumentException(MessageFactory.get("error.convert", val, String.class, type));
+    }
+
+    /**
+     * @param obj Object to be coerced
+     * @return The result of coercion
+     */
+    public final static String coerceToString(final Object obj) {
+        if (obj == null) {
+            return "";
+        } else if (obj instanceof String) {
+            return (String) obj;
+        } else if (obj instanceof Enum) {
+            return ((Enum) obj).name();
+        } else {
+            return obj.toString();
+        }
+    }
+
+    public final static void checkType(final Object obj, final Class<?> type) throws IllegalArgumentException {
+        if (String.class.equals(type)) {
+            coerceToString(obj);
+        }
+        if (ELArithmetic.isNumberType(type)) {
+            coerceToNumber(obj, type);
+        }
+        if (Character.class.equals(type) || Character.TYPE == type) {
+            coerceToCharacter(obj);
+        }
+        if (Boolean.class.equals(type) || Boolean.TYPE == type) {
+            coerceToBoolean(obj);
+        }
+        if (type.isEnum()) {
+            coerceToEnum(obj, type);
+        }
+    }
+
+    public final static Object coerceToType(final Object obj, final Class<?> type) throws IllegalArgumentException {
+        return coerceToType(obj, type, false);
+    }
+
+    public final static Object coerceToType(final Object obj, final Class<?> type, boolean isEL22Compatible) throws IllegalArgumentException {
+
+        if (type == null || Object.class.equals(type) || (obj != null && type.isAssignableFrom(obj.getClass()))) {
+            return obj;
+        }
+
+        // New in 3.0
+        if (!isEL22Compatible && obj == null && !type.isPrimitive() && !String.class.equals(type)) {
+            return null;
+        }
+
+        if (String.class.equals(type)) {
+            return coerceToString(obj);
+        }
+        if (ELArithmetic.isNumberType(type)) {
+            return coerceToNumber(obj, type);
+        }
+        if (Character.class.equals(type) || Character.TYPE == type) {
+            return coerceToCharacter(obj);
+        }
+        if (Boolean.class.equals(type) || Boolean.TYPE == type) {
+            return coerceToBoolean(obj);
+        }
+        if (type.isEnum()) {
+            return coerceToEnum(obj, type);
+        }
+
+        if (obj == null) {
+            return null;
+        }
+
+        if (obj instanceof String) {
+            if ("".equals(obj)) {
+                return null;
+            }
+            PropertyEditor editor = PropertyEditorManager.findEditor(type);
+            if (editor != null) {
+                editor.setAsText((String) obj);
+                return editor.getValue();
+            }
+        }
+        throw new IllegalArgumentException(MessageFactory.get("error.convert", obj, obj.getClass(), type));
+    }
+
+    /**
+     * @param obj An array of objects
+     * @return true if the array contains a null, false otherwise
+     */
+    public final static boolean containsNulls(final Object[] obj) {
+        for (int i = 0; i < obj.length; i++) {
+            if (obj[0] == null) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public final static boolean isBigDecimalOp(final Object obj0, final Object obj1) {
+        return (obj0 instanceof BigDecimal || obj1 instanceof BigDecimal);
+    }
+
+    public final static boolean isBigIntegerOp(final Object obj0, final Object obj1) {
+        return (obj0 instanceof BigInteger || obj1 instanceof BigInteger);
+    }
+
+    public final static boolean isDoubleOp(final Object obj0, final Object obj1) {
+        return (obj0 instanceof Double || obj1 instanceof Double || obj0 instanceof Float || obj1 instanceof Float);
+    }
+
+    public final static boolean isDoubleStringOp(final Object obj0, final Object obj1) {
+        return (isDoubleOp(obj0, obj1) || (obj0 instanceof String && isStringFloat((String) obj0)) || (obj1 instanceof String && isStringFloat((String) obj1)));
+    }
+
+    public final static boolean isLongOp(final Object obj0, final Object obj1) {
+        return (obj0 instanceof Long || obj1 instanceof Long || obj0 instanceof Integer || obj1 instanceof Integer || obj0 instanceof Character
+                || obj1 instanceof Character || obj0 instanceof Short || obj1 instanceof Short || obj0 instanceof Byte || obj1 instanceof Byte);
+    }
+
+    public final static boolean isStringFloat(final String str) {
+        int len = str.length();
+        if (len > 1) {
+            for (int i = 0; i < len; i++) {
+                switch (str.charAt(i)) {
+                case 'E':
+                    return true;
+                case 'e':
+                    return true;
+                case '.':
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public final static Number toFloat(final String value) {
+        try {
+            if (Double.parseDouble(value) > Double.MAX_VALUE) {
+                return new BigDecimal(value);
+            } else {
+                return Double.valueOf(value);
+            }
+        } catch (NumberFormatException e0) {
+            return new BigDecimal(value);
+        }
+    }
+
+    public final static Number toNumber(final String value) {
+        try {
+            return Integer.valueOf(Integer.parseInt(value));
+        } catch (NumberFormatException e0) {
+            try {
+                return Long.valueOf(Long.parseLong(value));
+            } catch (NumberFormatException e1) {
+                return new BigInteger(value);
+            }
+        }
+    }
+
+    /**
+     *
+     */
+    public ELSupport() {
+        super();
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/lang/EvaluationContext.java b/impl/src/main/java/com/sun/el/lang/EvaluationContext.java
new file mode 100644
index 0000000..4b07a60
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/lang/EvaluationContext.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.lang;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.el.ELContext;
+import javax.el.ELResolver;
+import javax.el.EvaluationListener;
+import javax.el.FunctionMapper;
+import javax.el.ImportHandler;
+import javax.el.VariableMapper;
+
+/**
+ * The context for Jakarta Expression expression evaluation. This wrapper ELContext captures the function mapper and the
+ * variable mapper at the point when the expression is parsed, and only for those functions and variable used in the
+ * expression.
+ */
+public final class EvaluationContext extends ELContext {
+
+    private final ELContext elContext;
+    private final FunctionMapper fnMapper;
+    private final VariableMapper varMapper;
+
+    public EvaluationContext(ELContext elContext, FunctionMapper fnMapper, VariableMapper varMapper) {
+        this.elContext = elContext;
+        this.fnMapper = fnMapper;
+        this.varMapper = varMapper;
+    }
+
+    public ELContext getELContext() {
+        return elContext;
+    }
+
+    @Override
+    public FunctionMapper getFunctionMapper() {
+        return fnMapper;
+    }
+
+    @Override
+    public VariableMapper getVariableMapper() {
+        return varMapper;
+    }
+
+    @Override
+    public Object getContext(Class key) {
+        return elContext.getContext(key);
+    }
+
+    @Override
+    public ELResolver getELResolver() {
+        return elContext.getELResolver();
+    }
+
+    @Override
+    public boolean isPropertyResolved() {
+        return elContext.isPropertyResolved();
+    }
+
+    @Override
+    public void putContext(Class key, Object contextObject) {
+        elContext.putContext(key, contextObject);
+    }
+
+    @Override
+    public void setPropertyResolved(boolean resolved) {
+        elContext.setPropertyResolved(resolved);
+    }
+
+    @Override
+    public void setPropertyResolved(Object base, Object property) {
+        elContext.setPropertyResolved(base, property);
+    }
+
+    @Override
+    public void addEvaluationListener(EvaluationListener listener) {
+        elContext.addEvaluationListener(listener);
+    }
+
+    @Override
+    public List<EvaluationListener> getEvaluationListeners() {
+        return elContext.getEvaluationListeners();
+    }
+
+    @Override
+    public void notifyBeforeEvaluation(String expr) {
+        elContext.notifyBeforeEvaluation(expr);
+    }
+
+    @Override
+    public void notifyAfterEvaluation(String expr) {
+        elContext.notifyAfterEvaluation(expr);
+    }
+
+    @Override
+    public void notifyPropertyResolved(Object base, Object property) {
+        elContext.notifyPropertyResolved(base, property);
+    }
+
+    @Override
+    public boolean isLambdaArgument(String arg) {
+        return elContext.isLambdaArgument(arg);
+    }
+
+    @Override
+    public Object getLambdaArgument(String arg) {
+        return elContext.getLambdaArgument(arg);
+    }
+
+    @Override
+    public void enterLambdaScope(Map<String, Object> args) {
+        elContext.enterLambdaScope(args);
+    }
+
+    @Override
+    public void exitLambdaScope() {
+        elContext.exitLambdaScope();
+    }
+
+    @Override
+    public Object convertToType(Object obj, Class<?> targetType) {
+        return elContext.convertToType(obj, targetType);
+    }
+
+    @Override
+    public ImportHandler getImportHandler() {
+        return elContext.getImportHandler();
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/lang/ExpressionBuilder.java b/impl/src/main/java/com/sun/el/lang/ExpressionBuilder.java
new file mode 100644
index 0000000..e1de322
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/lang/ExpressionBuilder.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.lang;
+
+import java.io.StringReader;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
+import java.lang.reflect.Method;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.el.ELContext;
+import javax.el.ELException;
+import javax.el.FunctionMapper;
+import javax.el.MethodExpression;
+import javax.el.ValueExpression;
+import javax.el.VariableMapper;
+
+import com.sun.el.MethodExpressionImpl;
+import com.sun.el.MethodExpressionLiteral;
+import com.sun.el.ValueExpressionImpl;
+import com.sun.el.parser.AstCompositeExpression;
+import com.sun.el.parser.AstDeferredExpression;
+import com.sun.el.parser.AstDynamicExpression;
+import com.sun.el.parser.AstFunction;
+import com.sun.el.parser.AstIdentifier;
+import com.sun.el.parser.AstLiteralExpression;
+import com.sun.el.parser.AstMethodArguments;
+import com.sun.el.parser.AstValue;
+import com.sun.el.parser.ELParser;
+import com.sun.el.parser.Node;
+import com.sun.el.parser.NodeVisitor;
+import com.sun.el.parser.ParseException;
+import com.sun.el.util.MessageFactory;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @author Kin-man Chung // EL cache
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class ExpressionBuilder implements NodeVisitor {
+
+    static private class NodeSoftReference extends SoftReference<Node> {
+        final String key;
+
+        NodeSoftReference(String key, Node node, ReferenceQueue<Node> refQ) {
+            super(node, refQ);
+            this.key = key;
+        }
+    }
+
+    static private class SoftConcurrentHashMap extends ConcurrentHashMap<String, Node> {
+
+        private static final int CACHE_INIT_SIZE = 256;
+        private ConcurrentHashMap<String, NodeSoftReference> map = new ConcurrentHashMap<String, NodeSoftReference>(CACHE_INIT_SIZE);
+        private ReferenceQueue<Node> refQ = new ReferenceQueue<Node>();
+
+        // Remove map entries that have been placed on the queue by GC.
+        private void cleanup() {
+            NodeSoftReference nodeRef = null;
+            while ((nodeRef = (NodeSoftReference) refQ.poll()) != null) {
+                map.remove(nodeRef.key);
+            }
+        }
+
+        @Override
+        public Node put(String key, Node value) {
+            cleanup();
+            NodeSoftReference prev = map.put(key, new NodeSoftReference(key, value, refQ));
+            return prev == null ? null : prev.get();
+        }
+
+        @Override
+        public Node putIfAbsent(String key, Node value) {
+            cleanup();
+            NodeSoftReference prev = map.putIfAbsent(key, new NodeSoftReference(key, value, refQ));
+            return prev == null ? null : prev.get();
+        }
+
+        @Override
+        public Node get(Object key) {
+            cleanup();
+            NodeSoftReference nodeRef = map.get(key);
+            if (nodeRef == null) {
+                return null;
+            }
+            if (nodeRef.get() == null) {
+                // value has been garbage collected, remove entry in map
+                map.remove(key);
+                return null;
+            }
+            return nodeRef.get();
+        }
+    }
+
+    private static final SoftConcurrentHashMap cache = new SoftConcurrentHashMap();
+    private FunctionMapper fnMapper;
+    private VariableMapper varMapper;
+    private String expression;
+
+    /**
+     *
+     */
+    public ExpressionBuilder(String expression, ELContext ctx) throws ELException {
+        this.expression = expression;
+
+        FunctionMapper ctxFn = ctx.getFunctionMapper();
+        VariableMapper ctxVar = ctx.getVariableMapper();
+
+        if (ctxFn != null) {
+            this.fnMapper = new FunctionMapperFactory(ctxFn);
+        }
+        if (ctxVar != null) {
+            this.varMapper = new VariableMapperFactory(ctxVar);
+        }
+    }
+
+    public static Node createNode(String expr) throws ELException {
+        Node n = createNodeInternal(expr);
+        return n;
+    }
+
+    private static Node createNodeInternal(String expr) throws ELException {
+        if (expr == null) {
+            throw new ELException(MessageFactory.get("error.null"));
+        }
+
+        Node n = cache.get(expr);
+        if (n == null) {
+            try {
+                n = (new ELParser(
+                        new com.sun.el.parser.ELParserTokenManager(new com.sun.el.parser.SimpleCharStream(new StringReader(expr), 1, 1, expr.length() + 1))))
+                                .CompositeExpression();
+
+                // validate composite expression
+                if (n instanceof AstCompositeExpression) {
+                    int numChildren = n.jjtGetNumChildren();
+                    if (numChildren == 1) {
+                        n = n.jjtGetChild(0);
+                    } else {
+                        Class type = null;
+                        Node child = null;
+                        for (int i = 0; i < numChildren; i++) {
+                            child = n.jjtGetChild(i);
+                            if (child instanceof AstLiteralExpression) {
+                                continue;
+                            }
+                            if (type == null) {
+                                type = child.getClass();
+                            } else {
+                                if (!type.equals(child.getClass())) {
+                                    throw new ELException(MessageFactory.get("error.mixed", expr));
+                                }
+                            }
+                        }
+                    }
+                }
+                if (n instanceof AstDeferredExpression || n instanceof AstDynamicExpression) {
+                    n = n.jjtGetChild(0);
+                }
+                cache.putIfAbsent(expr, n);
+            } catch (ParseException pe) {
+                throw new ELException("Error Parsing: " + expr, pe);
+            }
+        }
+        return n;
+    }
+
+    /**
+     * Scan the expression nodes and captures the functions and variables used in this expression. This ensures that any
+     * changes to the functions or variables mappings during the expression will not affect the evaluation of this
+     * expression, as the functions and variables are bound and resolved at parse time, as specified in the spec.
+     */
+    private void prepare(Node node) throws ELException {
+        node.accept(this);
+        if (this.fnMapper instanceof FunctionMapperFactory) {
+            this.fnMapper = ((FunctionMapperFactory) this.fnMapper).create();
+        }
+        if (this.varMapper instanceof VariableMapperFactory) {
+            this.varMapper = ((VariableMapperFactory) this.varMapper).create();
+        }
+    }
+
+    private Node build() throws ELException {
+        Node n = createNodeInternal(this.expression);
+        this.prepare(n);
+        if (n instanceof AstDeferredExpression || n instanceof AstDynamicExpression) {
+            n = n.jjtGetChild(0);
+        }
+        return n;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see com.sun.el.parser.NodeVisitor#visit(com.sun.el.parser.Node)
+     */
+    @Override
+    public void visit(Node node) throws ELException {
+        if (node instanceof AstFunction) {
+            AstFunction funcNode = (AstFunction) node;
+            if ((funcNode.getPrefix().length() == 0)
+                    && (this.fnMapper == null || fnMapper.resolveFunction(funcNode.getPrefix(), funcNode.getLocalName()) == null)) {
+                // This can be a call to a LambdaExpression. The target
+                // of the call is a bean or an Jakarta Expression variable. Capture
+                // the variable name in the variable mapper if it is an
+                // variable. The decision to invoke the static method or
+                // the LambdaExpression will be made at runtime.
+                if (this.varMapper != null) {
+                    this.varMapper.resolveVariable(funcNode.getLocalName());
+                }
+                return;
+            }
+
+            if (this.fnMapper == null) {
+                throw new ELException(MessageFactory.get("error.fnMapper.null"));
+            }
+            Method m = fnMapper.resolveFunction(funcNode.getPrefix(), funcNode.getLocalName());
+            if (m == null) {
+                throw new ELException(MessageFactory.get("error.fnMapper.method", funcNode.getOutputName()));
+            }
+            int pcnt = m.getParameterTypes().length;
+            int acnt = ((AstMethodArguments) node.jjtGetChild(0)).getParameterCount();
+            if (acnt != pcnt) {
+                throw new ELException(MessageFactory.get("error.fnMapper.paramcount", funcNode.getOutputName(), "" + pcnt, "" + acnt));
+            }
+        } else if (node instanceof AstIdentifier && this.varMapper != null) {
+            String variable = ((AstIdentifier) node).getImage();
+
+            // simply capture it
+            this.varMapper.resolveVariable(variable);
+        }
+    }
+
+    public ValueExpression createValueExpression(Class expectedType) throws ELException {
+        Node n = this.build();
+        return new ValueExpressionImpl(this.expression, n, this.fnMapper, this.varMapper, expectedType);
+    }
+
+    public MethodExpression createMethodExpression(Class expectedReturnType, Class[] expectedParamTypes) throws ELException {
+        Node n = this.build();
+        if (n instanceof AstValue || n instanceof AstIdentifier) {
+            return new MethodExpressionImpl(expression, n, this.fnMapper, this.varMapper, expectedReturnType, expectedParamTypes);
+        } else if (n instanceof AstLiteralExpression) {
+            return new MethodExpressionLiteral(expression, expectedReturnType, expectedParamTypes);
+        } else {
+            throw new ELException("Not a Valid Method Expression: " + expression);
+        }
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/lang/FunctionMapperFactory.java b/impl/src/main/java/com/sun/el/lang/FunctionMapperFactory.java
new file mode 100644
index 0000000..3bb0157
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/lang/FunctionMapperFactory.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.lang;
+
+import java.lang.reflect.Method;
+
+import javax.el.FunctionMapper;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public class FunctionMapperFactory extends FunctionMapper {
+
+    protected FunctionMapperImpl memento = null;
+    protected FunctionMapper target;
+
+    public FunctionMapperFactory(FunctionMapper mapper) {
+        if (mapper == null) {
+            throw new NullPointerException("FunctionMapper target cannot be null");
+        }
+        this.target = mapper;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see javax.el.FunctionMapper#resolveFunction(java.lang.String, java.lang.String)
+     */
+    @Override
+    public Method resolveFunction(String prefix, String localName) {
+        if (this.memento == null) {
+            this.memento = new FunctionMapperImpl();
+        }
+        Method m = this.target.resolveFunction(prefix, localName);
+        if (m != null) {
+            this.memento.addFunction(prefix, localName, m);
+        }
+        return m;
+    }
+
+    public FunctionMapper create() {
+        return this.memento;
+    }
+
+}
diff --git a/impl/src/main/java/com/sun/el/lang/FunctionMapperImpl.java b/impl/src/main/java/com/sun/el/lang/FunctionMapperImpl.java
new file mode 100644
index 0000000..970bc0e
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/lang/FunctionMapperImpl.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.lang;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.el.FunctionMapper;
+
+import com.sun.el.util.ReflectionUtil;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public class FunctionMapperImpl extends FunctionMapper implements Externalizable {
+
+    private static final long serialVersionUID = 1L;
+
+    protected Map<String, Function> functions = null;
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see javax.el.FunctionMapper#resolveFunction(java.lang.String, java.lang.String)
+     */
+    @Override
+    public Method resolveFunction(String prefix, String localName) {
+        if (this.functions != null) {
+            Function f = this.functions.get(prefix + ":" + localName);
+            return f.getMethod();
+        }
+        return null;
+    }
+
+    public void addFunction(String prefix, String localName, Method m) {
+        if (this.functions == null) {
+            this.functions = new HashMap<String, Function>();
+        }
+        Function f = new Function(prefix, localName, m);
+        synchronized (this) {
+            this.functions.put(prefix + ":" + localName, f);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
+     */
+    @Override
+    public void writeExternal(ObjectOutput out) throws IOException {
+        out.writeObject(this.functions);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.io.Externalizable#readExternal(java.io.ObjectInput)
+     */
+    // Safe cast
+    @Override
+    @SuppressWarnings("unchecked")
+    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+        this.functions = (Map<String, Function>) in.readObject();
+    }
+
+    public static class Function implements Externalizable {
+
+        protected transient Method m;
+        protected String owner;
+        protected String name;
+        protected String[] types;
+        protected String prefix;
+        protected String localName;
+
+        /**
+         *
+         */
+        public Function(String prefix, String localName, Method m) {
+            if (localName == null) {
+                throw new NullPointerException("LocalName cannot be null");
+            }
+            if (m == null) {
+                throw new NullPointerException("Method cannot be null");
+            }
+            this.prefix = prefix;
+            this.localName = localName;
+            this.m = m;
+        }
+
+        public Function() {
+            // for serialization
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
+         */
+        @Override
+        public void writeExternal(ObjectOutput out) throws IOException {
+
+            out.writeUTF((this.prefix != null) ? this.prefix : "");
+            out.writeUTF(this.localName);
+
+            if (this.owner != null) {
+                out.writeUTF(this.owner);
+            } else {
+                out.writeUTF(this.m.getDeclaringClass().getName());
+            }
+            if (this.name != null) {
+                out.writeUTF(this.name);
+            } else {
+                out.writeUTF(this.m.getName());
+            }
+            if (this.types != null) {
+                out.writeObject(this.types);
+            } else {
+                out.writeObject(ReflectionUtil.toTypeNameArray(this.m.getParameterTypes()));
+            }
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see java.io.Externalizable#readExternal(java.io.ObjectInput)
+         */
+        @Override
+        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+
+            this.prefix = in.readUTF();
+            if ("".equals(this.prefix)) {
+                this.prefix = null;
+            }
+            this.localName = in.readUTF();
+            this.owner = in.readUTF();
+            this.name = in.readUTF();
+            this.types = (String[]) in.readObject();
+        }
+
+        public Method getMethod() {
+            if (this.m == null) {
+                try {
+                    Class<?> t = Class.forName(this.owner, false, Thread.currentThread().getContextClassLoader());
+                    Class[] p = ReflectionUtil.toTypeArray(this.types);
+                    this.m = t.getMethod(this.name, p);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+            return this.m;
+        }
+
+        public boolean matches(String prefix, String localName) {
+            if (this.prefix != null) {
+                if (prefix == null) {
+                    return false;
+                }
+                if (!this.prefix.equals(prefix)) {
+                    return false;
+                }
+            }
+            return this.localName.equals(localName);
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof Function) {
+                return this.hashCode() == obj.hashCode();
+            }
+            return false;
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see java.lang.Object#hashCode()
+         */
+        @Override
+        public int hashCode() {
+            return (this.prefix + this.localName).hashCode();
+        }
+    }
+
+}
diff --git a/impl/src/main/java/com/sun/el/lang/VariableMapperFactory.java b/impl/src/main/java/com/sun/el/lang/VariableMapperFactory.java
new file mode 100644
index 0000000..b67aade
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/lang/VariableMapperFactory.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.lang;
+
+import javax.el.ValueExpression;
+import javax.el.VariableMapper;
+
+/**
+ * Creates a VariableMapper for the variables used in the expression.
+ */
+public class VariableMapperFactory extends VariableMapper {
+
+    private final VariableMapper target;
+    private VariableMapper momento;
+
+    public VariableMapperFactory(VariableMapper target) {
+        if (target == null) {
+            throw new NullPointerException("Target VariableMapper cannot be null");
+        }
+        this.target = target;
+    }
+
+    public VariableMapper create() {
+        return this.momento;
+    }
+
+    @Override
+    public ValueExpression resolveVariable(String variable) {
+        ValueExpression expr = this.target.resolveVariable(variable);
+        if (expr != null) {
+            if (this.momento == null) {
+                this.momento = new VariableMapperImpl();
+            }
+            this.momento.setVariable(variable, expr);
+        }
+        return expr;
+    }
+
+    @Override
+    public ValueExpression setVariable(String variable, ValueExpression expression) {
+        throw new UnsupportedOperationException("Cannot Set Variables on Factory");
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/lang/VariableMapperImpl.java b/impl/src/main/java/com/sun/el/lang/VariableMapperImpl.java
new file mode 100644
index 0000000..027bbe6
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/lang/VariableMapperImpl.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.lang;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.el.ValueExpression;
+import javax.el.VariableMapper;
+
+public class VariableMapperImpl extends VariableMapper implements Externalizable {
+
+    private static final long serialVersionUID = 1L;
+
+    private Map<String, ValueExpression> vars = new HashMap<String, ValueExpression>();
+
+    public VariableMapperImpl() {
+        super();
+    }
+
+    @Override
+    public ValueExpression resolveVariable(String variable) {
+        return this.vars.get(variable);
+    }
+
+    @Override
+    public ValueExpression setVariable(String variable, ValueExpression expression) {
+        return this.vars.put(variable, expression);
+    }
+
+    // Safe cast.
+    @Override
+    @SuppressWarnings("unchecked")
+    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+        this.vars = (Map<String, ValueExpression>) in.readObject();
+    }
+
+    @Override
+    public void writeExternal(ObjectOutput out) throws IOException {
+        out.writeObject(this.vars);
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/ArithmeticNode.java b/impl/src/main/java/com/sun/el/parser/ArithmeticNode.java
new file mode 100644
index 0000000..36fce35
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/ArithmeticNode.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public class ArithmeticNode extends SimpleNode {
+
+    /**
+     * @param i
+     */
+    public ArithmeticNode(int i) {
+        super(i);
+    }
+
+    @Override
+    public Class getType(EvaluationContext ctx) throws ELException {
+        return Number.class;
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstAnd.java b/impl/src/main/java/com/sun/el/parser/AstAnd.java
new file mode 100644
index 0000000..6e30086
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstAnd.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstAnd extends BooleanNode {
+    public AstAnd(int id) {
+        super(id);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        Object obj = children[0].getValue(ctx);
+        Boolean b = coerceToBoolean(obj);
+        if (!b.booleanValue()) {
+            return b;
+        }
+        obj = children[1].getValue(ctx);
+        b = coerceToBoolean(obj);
+        return b;
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstAssign.java b/impl/src/main/java/com/sun/el/parser/AstAssign.java
new file mode 100644
index 0000000..6ef715a
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstAssign.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+public class AstAssign extends SimpleNode {
+    public AstAssign(int id) {
+        super(id);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+
+        Object value = children[1].getValue(ctx);
+        children[0].setValue(ctx, value);
+        return value;
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstBracketSuffix.java b/impl/src/main/java/com/sun/el/parser/AstBracketSuffix.java
new file mode 100644
index 0000000..03454ab
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstBracketSuffix.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @author Kin-man Chung
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstBracketSuffix extends SimpleNode {
+    public AstBracketSuffix(int id) {
+        super(id);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        return this.children[0].getValue(ctx);
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstChoice.java b/impl/src/main/java/com/sun/el/parser/AstChoice.java
new file mode 100644
index 0000000..78f3fb4
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstChoice.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstChoice extends SimpleNode {
+    public AstChoice(int id) {
+        super(id);
+    }
+
+    @Override
+    public Class getType(EvaluationContext ctx) throws ELException {
+        Object obj0 = this.children[0].getValue(ctx);
+        Boolean b0 = coerceToBoolean(obj0);
+        return this.children[((b0.booleanValue() ? 1 : 2))].getType(ctx);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        Object obj0 = this.children[0].getValue(ctx);
+        Boolean b0 = coerceToBoolean(obj0);
+        return this.children[((b0.booleanValue() ? 1 : 2))].getValue(ctx);
+    }
+
+    @Override
+    public boolean isReadOnly(EvaluationContext ctx) throws ELException {
+        Object obj0 = this.children[0].getValue(ctx);
+        Boolean b0 = coerceToBoolean(obj0);
+        return this.children[((b0.booleanValue() ? 1 : 2))].isReadOnly(ctx);
+    }
+
+    @Override
+    public void setValue(EvaluationContext ctx, Object value) throws ELException {
+        Object obj0 = this.children[0].getValue(ctx);
+        Boolean b0 = coerceToBoolean(obj0);
+        this.children[((b0.booleanValue() ? 1 : 2))].setValue(ctx, value);
+    }
+
+    @Override
+    public Object invoke(EvaluationContext ctx, Class[] paramTypes, Object[] paramValues) throws ELException {
+        Object obj0 = this.children[0].getValue(ctx);
+        Boolean b0 = coerceToBoolean(obj0);
+        return this.children[((b0.booleanValue() ? 1 : 2))].invoke(ctx, paramTypes, paramValues);
+    }
+
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstCompositeExpression.java b/impl/src/main/java/com/sun/el/parser/AstCompositeExpression.java
new file mode 100644
index 0000000..b9f4a18
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstCompositeExpression.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstCompositeExpression extends SimpleNode {
+
+    public AstCompositeExpression(int id) {
+        super(id);
+    }
+
+    @Override
+    public Class getType(EvaluationContext ctx) throws ELException {
+        return String.class;
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        StringBuffer sb = new StringBuffer(16);
+        Object obj = null;
+        if (this.children != null) {
+            for (int i = 0; i < this.children.length; i++) {
+                obj = this.children[i].getValue(ctx);
+                if (obj != null) {
+                    sb.append(obj);
+                }
+            }
+        }
+        return sb.toString();
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstConcat.java b/impl/src/main/java/com/sun/el/parser/AstConcat.java
new file mode 100644
index 0000000..e0957f8
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstConcat.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Kin-man Chung
+ */
+public final class AstConcat extends SimpleNode {
+    public AstConcat(int id) {
+        super(id);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        return children[0].getValue(ctx).toString() + children[1].getValue(ctx).toString();
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstDeferredExpression.java b/impl/src/main/java/com/sun/el/parser/AstDeferredExpression.java
new file mode 100644
index 0000000..9992b44
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstDeferredExpression.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstDeferredExpression extends SimpleNode {
+    public AstDeferredExpression(int id) {
+        super(id);
+    }
+
+    @Override
+    public Class getType(EvaluationContext ctx) throws ELException {
+        return this.children[0].getType(ctx);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        return this.children[0].getValue(ctx);
+    }
+
+    @Override
+    public boolean isReadOnly(EvaluationContext ctx) throws ELException {
+        return this.children[0].isReadOnly(ctx);
+    }
+
+    @Override
+    public void setValue(EvaluationContext ctx, Object value) throws ELException {
+        this.children[0].setValue(ctx, value);
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstDiv.java b/impl/src/main/java/com/sun/el/parser/AstDiv.java
new file mode 100644
index 0000000..2979ac7
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstDiv.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.ELArithmetic;
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstDiv extends ArithmeticNode {
+    public AstDiv(int id) {
+        super(id);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        Object obj0 = this.children[0].getValue(ctx);
+        Object obj1 = this.children[1].getValue(ctx);
+        return ELArithmetic.divide(obj0, obj1);
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstDotSuffix.java b/impl/src/main/java/com/sun/el/parser/AstDotSuffix.java
new file mode 100644
index 0000000..17083cc
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstDotSuffix.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstDotSuffix extends SimpleNode {
+    public AstDotSuffix(int id) {
+        super(id);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        return this.image;
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstDynamicExpression.java b/impl/src/main/java/com/sun/el/parser/AstDynamicExpression.java
new file mode 100644
index 0000000..a9d5f8f
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstDynamicExpression.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstDynamicExpression extends SimpleNode {
+    public AstDynamicExpression(int id) {
+        super(id);
+    }
+
+    @Override
+    public Class getType(EvaluationContext ctx) throws ELException {
+        return this.children[0].getType(ctx);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        return this.children[0].getValue(ctx);
+    }
+
+    @Override
+    public boolean isReadOnly(EvaluationContext ctx) throws ELException {
+        return this.children[0].isReadOnly(ctx);
+    }
+
+    @Override
+    public void setValue(EvaluationContext ctx, Object value) throws ELException {
+        this.children[0].setValue(ctx, value);
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstEmpty.java b/impl/src/main/java/com/sun/el/parser/AstEmpty.java
new file mode 100644
index 0000000..847300a
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstEmpty.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import java.util.Collection;
+import java.util.Map;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstEmpty extends SimpleNode {
+    public AstEmpty(int id) {
+        super(id);
+    }
+
+    @Override
+    public Class getType(EvaluationContext ctx) throws ELException {
+        return Boolean.class;
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        Object obj = this.children[0].getValue(ctx);
+        if (obj == null) {
+            return Boolean.TRUE;
+        } else if (obj instanceof String) {
+            return Boolean.valueOf(((String) obj).length() == 0);
+        } else if (obj instanceof Object[]) {
+            return Boolean.valueOf(((Object[]) obj).length == 0);
+        } else if (obj instanceof Collection) {
+            return Boolean.valueOf(((Collection) obj).isEmpty());
+        } else if (obj instanceof Map) {
+            return Boolean.valueOf(((Map) obj).isEmpty());
+        }
+        return Boolean.FALSE;
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstEqual.java b/impl/src/main/java/com/sun/el/parser/AstEqual.java
new file mode 100644
index 0000000..ae26ef8
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstEqual.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstEqual extends BooleanNode {
+    public AstEqual(int id) {
+        super(id);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        Object obj0 = this.children[0].getValue(ctx);
+        Object obj1 = this.children[1].getValue(ctx);
+        return Boolean.valueOf(equals(obj0, obj1));
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstFalse.java b/impl/src/main/java/com/sun/el/parser/AstFalse.java
new file mode 100644
index 0000000..2c7ac5c
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstFalse.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstFalse extends BooleanNode {
+    public AstFalse(int id) {
+        super(id);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        return Boolean.FALSE;
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstFloatingPoint.java b/impl/src/main/java/com/sun/el/parser/AstFloatingPoint.java
new file mode 100644
index 0000000..bf3d003
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstFloatingPoint.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import java.math.BigDecimal;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstFloatingPoint extends SimpleNode {
+    public AstFloatingPoint(int id) {
+        super(id);
+    }
+
+    private Number number;
+
+    public Number getFloatingPoint() {
+        if (this.number == null) {
+            try {
+                this.number = Double.valueOf(this.image);
+            } catch (ArithmeticException e0) {
+                this.number = new BigDecimal(this.image);
+            }
+        }
+        return this.number;
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        return this.getFloatingPoint();
+    }
+
+    @Override
+    public Class getType(EvaluationContext ctx) throws ELException {
+        return this.getFloatingPoint().getClass();
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstFunction.java b/impl/src/main/java/com/sun/el/parser/AstFunction.java
new file mode 100644
index 0000000..15fb1af
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstFunction.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import javax.el.ELClass;
+import javax.el.ELException;
+import javax.el.FunctionMapper;
+import javax.el.LambdaExpression;
+import javax.el.ValueExpression;
+import javax.el.VariableMapper;
+
+import com.sun.el.lang.EvaluationContext;
+import com.sun.el.util.MessageFactory;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstFunction extends SimpleNode {
+
+    protected String localName = "";
+
+    protected String prefix = "";
+
+    public AstFunction(int id) {
+        super(id);
+    }
+
+    public String getLocalName() {
+        return localName;
+    }
+
+    public String getOutputName() {
+        if (this.prefix.length() == 0) {
+            return this.localName;
+        } else {
+            return this.prefix + ":" + this.localName;
+        }
+    }
+
+    public String getPrefix() {
+        return prefix;
+    }
+
+    @Override
+    public Class getType(EvaluationContext ctx) throws ELException {
+
+        FunctionMapper fnMapper = ctx.getFunctionMapper();
+
+        // quickly validate again for this request
+        if (fnMapper == null) {
+            throw new ELException(MessageFactory.get("error.fnMapper.null"));
+        }
+        Method m = fnMapper.resolveFunction(this.prefix, this.localName);
+        if (m == null) {
+            throw new ELException(MessageFactory.get("error.fnMapper.method", this.getOutputName()));
+        }
+        return m.getReturnType();
+    }
+
+    /*
+     * Find the object associated with the given name. Return null if the there is no such object.
+     */
+    private Object findValue(EvaluationContext ctx, String name) {
+        Object value;
+        // First check if this is a Lambda argument
+        if (ctx.isLambdaArgument(name)) {
+            return ctx.getLambdaArgument(name);
+        }
+
+        // Next check if this an Jakarta Expression variable
+        VariableMapper varMapper = ctx.getVariableMapper();
+        if (varMapper != null) {
+            ValueExpression expr = varMapper.resolveVariable(name);
+            if (expr != null) {
+                return expr.getValue(ctx.getELContext());
+            }
+        }
+        // Check if this is resolvable by an ELResolver
+        ctx.setPropertyResolved(false);
+        Object ret = ctx.getELResolver().getValue(ctx, null, name);
+        if (ctx.isPropertyResolved()) {
+            return ret;
+        }
+        return null;
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+
+        // Check to see if a function is a bean that is a Lambdaexpression.
+        // If so, invoke it. Also allow for the case that a Lambda expression
+        // can return another Lambda expression.
+        if (prefix.length() == 0) {
+            Object val = findValue(ctx, this.localName);
+            // Check the case of repeated lambda invocation, such as f()()()
+
+            if ((val != null) && (val instanceof LambdaExpression)) {
+                for (int i = 0; i < this.children.length; i++) {
+                    Object[] params = ((AstMethodArguments) this.children[i]).getParameters(ctx);
+                    if (!(val instanceof LambdaExpression)) {
+                        throw new ELException(MessageFactory.get("error.function.syntax", getOutputName()));
+                    }
+                    val = ((LambdaExpression) val).invoke(ctx, params);
+                }
+                return val;
+            }
+        }
+
+        FunctionMapper fnMapper = ctx.getFunctionMapper();
+
+        Method m = null;
+        if (fnMapper != null) {
+            m = fnMapper.resolveFunction(this.prefix, this.localName);
+        }
+        if (m == null) {
+            if (this.prefix.length() == 0 && ctx.getImportHandler() != null) {
+                Class<?> c = null;
+                ;
+                // Check if this is a constructor call for an imported class
+                c = ctx.getImportHandler().resolveClass(this.localName);
+                String methodName = null;
+                if (c != null) {
+                    methodName = "<init>";
+                } else {
+                    // Check if this is a imported static method
+                    c = ctx.getImportHandler().resolveStatic(this.localName);
+                    methodName = this.localName;
+                    ;
+                }
+                if (c != null) {
+                    // Use StaticFieldELResolver to invoke the constructor or the
+                    // static method.
+                    Object[] params = ((AstMethodArguments) this.children[0]).getParameters(ctx);
+                    return ctx.getELResolver().invoke(ctx, new ELClass(c), methodName, null, params);
+                }
+            }
+            // quickly validate for this request
+            if (fnMapper == null) {
+                throw new ELException(MessageFactory.get("error.fnMapper.null"));
+            }
+            throw new ELException(MessageFactory.get("error.fnMapper.method", this.getOutputName()));
+        }
+
+        Class[] paramTypes = m.getParameterTypes();
+        Object[] params = ((AstMethodArguments) this.children[0]).getParameters(ctx);
+        Object result = null;
+        for (int i = 0; i < params.length; i++) {
+            try {
+                params[i] = ctx.convertToType(params[i], paramTypes[i]);
+            } catch (ELException ele) {
+                throw new ELException(MessageFactory.get("error.function", this.getOutputName()), ele);
+            }
+        }
+        try {
+            result = m.invoke(null, params);
+        } catch (IllegalAccessException iae) {
+            throw new ELException(MessageFactory.get("error.function", this.getOutputName()), iae);
+        } catch (InvocationTargetException ite) {
+            throw new ELException(MessageFactory.get("error.function", this.getOutputName()), ite.getCause());
+        }
+        return result;
+    }
+
+    public void setLocalName(String localName) {
+        this.localName = localName;
+    }
+
+    public void setPrefix(String prefix) {
+        this.prefix = prefix;
+    }
+
+    @Override
+    public String toString() {
+        return ELParserTreeConstants.jjtNodeName[id] + "[" + this.getOutputName() + "]";
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstGreaterThan.java b/impl/src/main/java/com/sun/el/parser/AstGreaterThan.java
new file mode 100644
index 0000000..2de0bd9
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstGreaterThan.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstGreaterThan extends BooleanNode {
+    public AstGreaterThan(int id) {
+        super(id);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        Object obj0 = this.children[0].getValue(ctx);
+        if (obj0 == null) {
+            return Boolean.FALSE;
+        }
+        Object obj1 = this.children[1].getValue(ctx);
+        if (obj1 == null) {
+            return Boolean.FALSE;
+        }
+        return (compare(obj0, obj1) > 0) ? Boolean.TRUE : Boolean.FALSE;
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstGreaterThanEqual.java b/impl/src/main/java/com/sun/el/parser/AstGreaterThanEqual.java
new file mode 100644
index 0000000..06252c0
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstGreaterThanEqual.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstGreaterThanEqual extends BooleanNode {
+    public AstGreaterThanEqual(int id) {
+        super(id);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        Object obj0 = this.children[0].getValue(ctx);
+        Object obj1 = this.children[1].getValue(ctx);
+        if (obj0 == obj1) {
+            return Boolean.TRUE;
+        }
+        if (obj0 == null || obj1 == null) {
+            return Boolean.FALSE;
+        }
+        return (compare(obj0, obj1) >= 0) ? Boolean.TRUE : Boolean.FALSE;
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstIdentifier.java b/impl/src/main/java/com/sun/el/parser/AstIdentifier.java
new file mode 100644
index 0000000..a5a149b
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstIdentifier.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELClass;
+import javax.el.ELException;
+import javax.el.ELResolver;
+import javax.el.MethodExpression;
+import javax.el.MethodInfo;
+import javax.el.MethodNotFoundException;
+import javax.el.PropertyNotWritableException;
+import javax.el.ValueExpression;
+import javax.el.ValueReference;
+import javax.el.VariableMapper;
+
+import com.sun.el.lang.ELSupport;
+import com.sun.el.lang.EvaluationContext;
+import com.sun.el.util.MessageFactory;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @author Kin-man Chung
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstIdentifier extends SimpleNode {
+    public AstIdentifier(int id) {
+        super(id);
+    }
+
+    @Override
+    public Class getType(EvaluationContext ctx) throws ELException {
+        // First check if this is a lambda argument
+        if (ctx.isLambdaArgument(this.image)) {
+            return Object.class;
+        }
+        VariableMapper varMapper = ctx.getVariableMapper();
+        if (varMapper != null) {
+            ValueExpression expr = varMapper.resolveVariable(this.image);
+            if (expr != null) {
+                return expr.getType(ctx.getELContext());
+            }
+        }
+        ctx.setPropertyResolved(false);
+        Class ret = ctx.getELResolver().getType(ctx, null, this.image);
+        if (!ctx.isPropertyResolved()) {
+            ELSupport.throwUnhandled(null, this.image);
+        }
+        return ret;
+    }
+
+    @Override
+    public ValueReference getValueReference(EvaluationContext ctx) throws ELException {
+        VariableMapper varMapper = ctx.getVariableMapper();
+        if (varMapper != null) {
+            ValueExpression expr = varMapper.resolveVariable(this.image);
+            if (expr != null) {
+                return expr.getValueReference(ctx.getELContext());
+            }
+        }
+        return new ValueReference(null, this.image);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        // First check if this is a lambda argument
+        if (ctx.isLambdaArgument(this.image)) {
+            return ctx.getLambdaArgument(this.image);
+        }
+        VariableMapper varMapper = ctx.getVariableMapper();
+        if (varMapper != null) {
+            ValueExpression expr = varMapper.resolveVariable(this.image);
+            if (expr != null) {
+                return expr.getValue(ctx.getELContext());
+            }
+        }
+        ctx.setPropertyResolved(false);
+        Object ret = ctx.getELResolver().getValue(ctx, null, this.image);
+        if (!ctx.isPropertyResolved()) {
+            // Check if this is an imported static field
+            if (ctx.getImportHandler() != null) {
+                Class<?> c = ctx.getImportHandler().resolveStatic(this.image);
+                if (c != null) {
+                    return ctx.getELResolver().getValue(ctx, new ELClass(c), this.image);
+                }
+            }
+            ELSupport.throwUnhandled(null, this.image);
+        }
+        return ret;
+    }
+
+    @Override
+    public boolean isReadOnly(EvaluationContext ctx) throws ELException {
+        // Lambda arguments are read only.
+        if (ctx.isLambdaArgument(this.image)) {
+            return true;
+        }
+        VariableMapper varMapper = ctx.getVariableMapper();
+        if (varMapper != null) {
+            ValueExpression expr = varMapper.resolveVariable(this.image);
+            if (expr != null) {
+                return expr.isReadOnly(ctx.getELContext());
+            }
+        }
+        ctx.setPropertyResolved(false);
+        boolean ret = ctx.getELResolver().isReadOnly(ctx, null, this.image);
+        if (!ctx.isPropertyResolved()) {
+            ELSupport.throwUnhandled(null, this.image);
+        }
+        return ret;
+    }
+
+    @Override
+    public void setValue(EvaluationContext ctx, Object value) throws ELException {
+        // First check if this is a lambda argument
+        if (ctx.isLambdaArgument(this.image)) {
+            throw new PropertyNotWritableException(MessageFactory.get("error.lambda.parameter.readonly", this.image));
+        }
+        VariableMapper varMapper = ctx.getVariableMapper();
+        if (varMapper != null) {
+            ValueExpression expr = varMapper.resolveVariable(this.image);
+            if (expr != null) {
+                expr.setValue(ctx.getELContext(), value);
+                return;
+            }
+        }
+        ctx.setPropertyResolved(false);
+        ELResolver elResolver = ctx.getELResolver();
+        elResolver.setValue(ctx, null, this.image, value);
+        if (!ctx.isPropertyResolved()) {
+            ELSupport.throwUnhandled(null, this.image);
+        }
+    }
+
+    private Object invokeTarget(EvaluationContext ctx, Object target, Object[] paramValues) throws ELException {
+        if (target instanceof MethodExpression) {
+            MethodExpression me = (MethodExpression) target;
+            return me.invoke(ctx.getELContext(), paramValues);
+        } else if (target == null) {
+            throw new MethodNotFoundException("Identity '" + this.image + "' was null and was unable to invoke");
+        } else {
+            throw new ELException(
+                    "Identity '" + this.image + "' does not reference a MethodExpression instance, returned type: " + target.getClass().getName());
+        }
+    }
+
+    @Override
+    public Object invoke(EvaluationContext ctx, Class[] paramTypes, Object[] paramValues) throws ELException {
+        return this.getMethodExpression(ctx).invoke(ctx.getELContext(), paramValues);
+    }
+
+    @Override
+    public MethodInfo getMethodInfo(EvaluationContext ctx, Class[] paramTypes) throws ELException {
+        return this.getMethodExpression(ctx).getMethodInfo(ctx.getELContext());
+    }
+
+    private MethodExpression getMethodExpression(EvaluationContext ctx) throws ELException {
+        Object obj = null;
+
+        // case A: ValueExpression exists, getValue which must
+        // be a MethodExpression
+        VariableMapper varMapper = ctx.getVariableMapper();
+        ValueExpression ve = null;
+        if (varMapper != null) {
+            ve = varMapper.resolveVariable(this.image);
+            if (ve != null) {
+                obj = ve.getValue(ctx);
+            }
+        }
+
+        // case B: evaluate the identity against the ELResolver, again, must be
+        // a MethodExpression to be able to invoke
+        if (ve == null) {
+            ctx.setPropertyResolved(false);
+            obj = ctx.getELResolver().getValue(ctx, null, this.image);
+        }
+
+        // finally provide helpful hints
+        if (obj instanceof MethodExpression) {
+            return (MethodExpression) obj;
+        } else if (obj == null) {
+            throw new MethodNotFoundException("Identity '" + this.image + "' was null and was unable to invoke");
+        } else {
+            throw new ELException("Identity '" + this.image + "' does not reference a MethodExpression instance, returned type: " + obj.getClass().getName());
+        }
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstInteger.java b/impl/src/main/java/com/sun/el/parser/AstInteger.java
new file mode 100644
index 0000000..8c6d7c7
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstInteger.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import java.math.BigInteger;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstInteger extends SimpleNode {
+    public AstInteger(int id) {
+        super(id);
+    }
+
+    private Number number;
+
+    protected Number getInteger() {
+        if (this.number == null) {
+            try {
+                this.number = Long.valueOf(this.image);
+            } catch (ArithmeticException e1) {
+                this.number = new BigInteger(this.image);
+            }
+        }
+        return number;
+    }
+
+    @Override
+    public Class getType(EvaluationContext ctx) throws ELException {
+        return this.getInteger().getClass();
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        return this.getInteger();
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstLambdaExpression.java b/impl/src/main/java/com/sun/el/parser/AstLambdaExpression.java
new file mode 100644
index 0000000..1349899
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstLambdaExpression.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import java.util.List;
+
+import javax.el.ELException;
+import javax.el.LambdaExpression;
+import javax.el.ValueExpression;
+
+import com.sun.el.ValueExpressionImpl;
+import com.sun.el.lang.EvaluationContext;
+import com.sun.el.util.MessageFactory;
+
+/**
+ * @author Kin-man Chung
+ */
+public class AstLambdaExpression extends SimpleNode {
+
+    public AstLambdaExpression(int id) {
+        super(id);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        // Create a lambda expression
+        ValueExpression expr = new ValueExpressionImpl("#{Lambda Expression}", this.children[1], ctx.getFunctionMapper(), ctx.getVariableMapper(), null);
+        List<String> parameters = ((AstLambdaParameters) this.children[0]).getParameters();
+        LambdaExpression lambda = new LambdaExpression(parameters, expr);
+        if (this.children.length <= 2) {
+            return lambda;
+        }
+
+        // There are arguments following the lambda exprn, invoke it now.
+        Object ret = null;
+        for (int i = 2; i < this.children.length; i++) {
+            if (ret != null) {
+                if (!(ret instanceof LambdaExpression)) {
+                    throw new ELException(MessageFactory.get("error.lambda.call"));
+                }
+                lambda = (LambdaExpression) ret;
+            }
+            AstMethodArguments args = (AstMethodArguments) this.children[i];
+            ret = lambda.invoke(ctx, args.getParameters(ctx));
+        }
+        return ret;
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstLambdaParameters.java b/impl/src/main/java/com/sun/el/parser/AstLambdaParameters.java
new file mode 100644
index 0000000..ee5875c
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstLambdaParameters.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Kin-man Chung
+ */
+
+public class AstLambdaParameters extends SimpleNode {
+    public AstLambdaParameters(int id) {
+        super(id);
+    }
+
+    List<String> getParameters() {
+        List<String> parameters = new ArrayList<String>();
+        if (children != null) {
+            for (Node child : children) {
+                parameters.add(child.getImage());
+            }
+        }
+        return parameters;
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstLessThan.java b/impl/src/main/java/com/sun/el/parser/AstLessThan.java
new file mode 100644
index 0000000..c1bd471
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstLessThan.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstLessThan extends BooleanNode {
+    public AstLessThan(int id) {
+        super(id);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        Object obj0 = this.children[0].getValue(ctx);
+        if (obj0 == null) {
+            return Boolean.FALSE;
+        }
+        Object obj1 = this.children[1].getValue(ctx);
+        if (obj1 == null) {
+            return Boolean.FALSE;
+        }
+        return (compare(obj0, obj1) < 0) ? Boolean.TRUE : Boolean.FALSE;
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstLessThanEqual.java b/impl/src/main/java/com/sun/el/parser/AstLessThanEqual.java
new file mode 100644
index 0000000..8f2c837
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstLessThanEqual.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstLessThanEqual extends BooleanNode {
+    public AstLessThanEqual(int id) {
+        super(id);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        Object obj0 = this.children[0].getValue(ctx);
+        Object obj1 = this.children[1].getValue(ctx);
+        if (obj0 == obj1) {
+            return Boolean.TRUE;
+        }
+        if (obj0 == null || obj1 == null) {
+            return Boolean.FALSE;
+        }
+        return (compare(obj0, obj1) <= 0) ? Boolean.TRUE : Boolean.FALSE;
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstListData.java b/impl/src/main/java/com/sun/el/parser/AstListData.java
new file mode 100644
index 0000000..7dc2952
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstListData.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import java.util.ArrayList;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Kin-man Chung
+ */
+public class AstListData extends SimpleNode {
+    public AstListData(int id) {
+        super(id);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) {
+        ArrayList<Object> list = new ArrayList<Object>();
+        int paramCount = this.jjtGetNumChildren();
+        for (int i = 0; i < paramCount; i++) {
+            list.add(this.children[i].getValue(ctx));
+        }
+        return list;
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstLiteralExpression.java b/impl/src/main/java/com/sun/el/parser/AstLiteralExpression.java
new file mode 100644
index 0000000..9672246
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstLiteralExpression.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstLiteralExpression extends SimpleNode {
+    public AstLiteralExpression(int id) {
+        super(id);
+    }
+
+    @Override
+    public Class getType(EvaluationContext ctx) throws ELException {
+        return String.class;
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        return this.image;
+    }
+
+    @Override
+    public void setImage(String image) {
+        if (image.indexOf('\\') == -1) {
+            this.image = image;
+            return;
+        }
+        int size = image.length();
+        StringBuffer buf = new StringBuffer(size);
+        for (int i = 0; i < size; i++) {
+            char c = image.charAt(i);
+            if (c == '\\' && i + 1 < size) {
+                char c1 = image.charAt(i + 1);
+                if (c1 == '\\' || c1 == '"' || c1 == '\'' || c1 == '#' || c1 == '$') {
+                    c = c1;
+                    i++;
+                }
+            }
+            buf.append(c);
+        }
+        this.image = buf.toString();
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstMapData.java b/impl/src/main/java/com/sun/el/parser/AstMapData.java
new file mode 100644
index 0000000..41435c8
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstMapData.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import java.util.HashMap;
+import java.util.HashSet;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Kin-man Chung
+ */
+public class AstMapData extends SimpleNode {
+    public AstMapData(int id) {
+        super(id);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) {
+        HashSet<Object> set = new HashSet<Object>();
+        HashMap<Object, Object> map = new HashMap<Object, Object>();
+
+        int paramCount = this.jjtGetNumChildren();
+        for (int i = 0; i < paramCount; i++) {
+            Node entry = this.children[i];
+            Object v1 = entry.jjtGetChild(0).getValue(ctx);
+            if (entry.jjtGetNumChildren() > 1) {
+                // expr: expr
+                map.put(v1, entry.jjtGetChild(1).getValue(ctx));
+            } else {
+                set.add(v1);
+            }
+        }
+        // It is error to have mixed set/map entries
+        if (set.size() > 0 && map.size() > 0) {
+            throw new ELException("Cannot mix set entry with map entry.");
+        }
+        if (map.size() > 0) {
+            return map;
+        }
+        return set;
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstMapEntry.java b/impl/src/main/java/com/sun/el/parser/AstMapEntry.java
new file mode 100644
index 0000000..c55caa6
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstMapEntry.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+/**
+ * @author Kin-man Chung
+ */
+public class AstMapEntry extends SimpleNode {
+    public AstMapEntry(int id) {
+        super(id);
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstMethodArguments.java b/impl/src/main/java/com/sun/el/parser/AstMethodArguments.java
new file mode 100644
index 0000000..24ef021
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstMethodArguments.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Kin-man Chung
+ */
+public class AstMethodArguments extends SimpleNode {
+    public AstMethodArguments(int id) {
+        super(id);
+    }
+
+    Class<?>[] getParamTypes() {
+        return null;
+    }
+
+    public Object[] getParameters(EvaluationContext ctx) throws ELException {
+
+        if (this.children == null) {
+            return new Object[] {};
+        }
+
+        Object[] obj = new Object[this.children.length];
+        for (int i = 0; i < obj.length; i++) {
+            obj[i] = this.children[i].getValue(ctx);
+        }
+        return obj;
+    }
+
+    public int getParameterCount() {
+        return this.children == null ? 0 : this.children.length;
+    }
+
+    @Override
+    public boolean isParametersProvided() {
+        return true;
+    }
+
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstMinus.java b/impl/src/main/java/com/sun/el/parser/AstMinus.java
new file mode 100644
index 0000000..11fdf84
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstMinus.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.ELArithmetic;
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstMinus extends ArithmeticNode {
+    public AstMinus(int id) {
+        super(id);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        Object obj0 = this.children[0].getValue(ctx);
+        Object obj1 = this.children[1].getValue(ctx);
+        return ELArithmetic.subtract(obj0, obj1);
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstMod.java b/impl/src/main/java/com/sun/el/parser/AstMod.java
new file mode 100644
index 0000000..d69b928
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstMod.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.ELArithmetic;
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstMod extends ArithmeticNode {
+    public AstMod(int id) {
+        super(id);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        Object obj0 = this.children[0].getValue(ctx);
+        Object obj1 = this.children[1].getValue(ctx);
+        return ELArithmetic.mod(obj0, obj1);
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstMult.java b/impl/src/main/java/com/sun/el/parser/AstMult.java
new file mode 100644
index 0000000..8ee4751
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstMult.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.ELArithmetic;
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstMult extends ArithmeticNode {
+    public AstMult(int id) {
+        super(id);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        Object obj0 = this.children[0].getValue(ctx);
+        Object obj1 = this.children[1].getValue(ctx);
+        return ELArithmetic.multiply(obj0, obj1);
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstNegative.java b/impl/src/main/java/com/sun/el/parser/AstNegative.java
new file mode 100644
index 0000000..e4b4465
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstNegative.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstNegative extends SimpleNode {
+    public AstNegative(int id) {
+        super(id);
+    }
+
+    @Override
+    public Class getType(EvaluationContext ctx) throws ELException {
+        return Number.class;
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        Object obj = this.children[0].getValue(ctx);
+
+        if (obj == null) {
+            return Long.valueOf(0);
+        }
+        if (obj instanceof BigDecimal) {
+            return ((BigDecimal) obj).negate();
+        }
+        if (obj instanceof BigInteger) {
+            return ((BigInteger) obj).negate();
+        }
+        if (obj instanceof String) {
+            if (isStringFloat((String) obj)) {
+                return Double.valueOf(-Double.parseDouble((String) obj));
+            }
+            return Long.valueOf(-Long.parseLong((String) obj));
+        }
+        Class type = obj.getClass();
+        if (obj instanceof Long || Long.TYPE == type) {
+            return Long.valueOf(-((Long) obj).longValue());
+        }
+        if (obj instanceof Double || Double.TYPE == type) {
+            return Double.valueOf(-((Double) obj).doubleValue());
+        }
+        if (obj instanceof Integer || Integer.TYPE == type) {
+            return Integer.valueOf(-((Integer) obj).intValue());
+        }
+        if (obj instanceof Float || Float.TYPE == type) {
+            return Float.valueOf(-((Float) obj).floatValue());
+        }
+        if (obj instanceof Short || Short.TYPE == type) {
+            return Short.valueOf((short) -((Short) obj).shortValue());
+        }
+        if (obj instanceof Byte || Byte.TYPE == type) {
+            return Byte.valueOf((byte) -((Byte) obj).byteValue());
+        }
+        Long num = (Long) coerceToNumber(obj, Long.class);
+        return Long.valueOf(-num.longValue());
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstNot.java b/impl/src/main/java/com/sun/el/parser/AstNot.java
new file mode 100644
index 0000000..5f0886b
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstNot.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstNot extends SimpleNode {
+    public AstNot(int id) {
+        super(id);
+    }
+
+    @Override
+    public Class getType(EvaluationContext ctx) throws ELException {
+        return Boolean.class;
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        Object obj = this.children[0].getValue(ctx);
+        Boolean b = coerceToBoolean(obj);
+        return Boolean.valueOf(!b.booleanValue());
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstNotEqual.java b/impl/src/main/java/com/sun/el/parser/AstNotEqual.java
new file mode 100644
index 0000000..359f479
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstNotEqual.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstNotEqual extends BooleanNode {
+    public AstNotEqual(int id) {
+        super(id);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        Object obj0 = this.children[0].getValue(ctx);
+        Object obj1 = this.children[1].getValue(ctx);
+        return Boolean.valueOf(!equals(obj0, obj1));
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstNull.java b/impl/src/main/java/com/sun/el/parser/AstNull.java
new file mode 100644
index 0000000..548a387
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstNull.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstNull extends SimpleNode {
+    public AstNull(int id) {
+        super(id);
+    }
+
+    @Override
+    public Class getType(EvaluationContext ctx) throws ELException {
+        return null;
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        return null;
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstOr.java b/impl/src/main/java/com/sun/el/parser/AstOr.java
new file mode 100644
index 0000000..2fc12d4
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstOr.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstOr extends BooleanNode {
+    public AstOr(int id) {
+        super(id);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        Object obj = this.children[0].getValue(ctx);
+        Boolean b = coerceToBoolean(obj);
+        if (b.booleanValue()) {
+            return b;
+        }
+        obj = this.children[1].getValue(ctx);
+        b = coerceToBoolean(obj);
+        return b;
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstPlus.java b/impl/src/main/java/com/sun/el/parser/AstPlus.java
new file mode 100644
index 0000000..2592188
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstPlus.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.ELArithmetic;
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @author Kin-man Chung
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstPlus extends ArithmeticNode {
+    public AstPlus(int id) {
+        super(id);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        Object obj0 = this.children[0].getValue(ctx);
+        Object obj1 = this.children[1].getValue(ctx);
+        return ELArithmetic.add(obj0, obj1);
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstSemiColon.java b/impl/src/main/java/com/sun/el/parser/AstSemiColon.java
new file mode 100644
index 0000000..275aaba
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstSemiColon.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Kin-man Chung
+ */
+public class AstSemiColon extends SimpleNode {
+    public AstSemiColon(int id) {
+        super(id);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        this.children[0].getValue(ctx);
+        return this.children[1].getValue(ctx);
+    }
+
+    @Override
+    public void setValue(EvaluationContext ctx, Object value) throws ELException {
+        this.children[0].getValue(ctx);
+        this.children[1].setValue(ctx, value);
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstString.java b/impl/src/main/java/com/sun/el/parser/AstString.java
new file mode 100644
index 0000000..77a7ad1
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstString.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstString extends SimpleNode {
+    public AstString(int id) {
+        super(id);
+    }
+
+    private String string;
+
+    public String getString() {
+        if (this.string == null) {
+            this.string = this.image.substring(1, this.image.length() - 1);
+        }
+        return this.string;
+    }
+
+    @Override
+    public Class getType(EvaluationContext ctx) throws ELException {
+        return String.class;
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        return this.getString();
+    }
+
+    @Override
+    public void setImage(String image) {
+        if (image.indexOf('\\') == -1) {
+            this.image = image;
+            return;
+        }
+        int size = image.length();
+        StringBuffer buf = new StringBuffer(size);
+        for (int i = 0; i < size; i++) {
+            char c = image.charAt(i);
+            if (c == '\\' && i + 1 < size) {
+                char c1 = image.charAt(i + 1);
+                if (c1 == '\\' || c1 == '"' || c1 == '\'' || c1 == '#' || c1 == '$') {
+                    c = c1;
+                    i++;
+                }
+            }
+            buf.append(c);
+        }
+        this.image = buf.toString();
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstTrue.java b/impl/src/main/java/com/sun/el/parser/AstTrue.java
new file mode 100644
index 0000000..607e6bd
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstTrue.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstTrue extends BooleanNode {
+    public AstTrue(int id) {
+        super(id);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        return Boolean.TRUE;
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/AstValue.java b/impl/src/main/java/com/sun/el/parser/AstValue.java
new file mode 100644
index 0000000..dfe0f82
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstValue.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import java.lang.reflect.Method;
+
+import javax.el.ELClass;
+import javax.el.ELException;
+import javax.el.ELResolver;
+import javax.el.ImportHandler;
+import javax.el.MethodInfo;
+import javax.el.PropertyNotFoundException;
+import javax.el.PropertyNotWritableException;
+import javax.el.ValueReference;
+
+import com.sun.el.lang.ELSupport;
+import com.sun.el.lang.EvaluationContext;
+import com.sun.el.util.MessageFactory;
+import com.sun.el.util.ReflectionUtil;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @author Kin-man Chung
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class AstValue extends SimpleNode {
+
+    protected static class Target {
+        protected Object base;
+        protected Node suffixNode;
+
+        Target(Object base, Node suffixNode) {
+            this.base = base;
+            this.suffixNode = suffixNode;
+        }
+
+        boolean isMethodCall() {
+            return getArguments(suffixNode) != null;
+        }
+    }
+
+    public AstValue(int id) {
+        super(id);
+    }
+
+    @Override
+    public Class getType(EvaluationContext ctx) throws ELException {
+        Target t = getTarget(ctx);
+        if (t.isMethodCall()) {
+            return null;
+        }
+        Object property = t.suffixNode.getValue(ctx);
+        ctx.setPropertyResolved(false);
+        Class ret = ctx.getELResolver().getType(ctx, t.base, property);
+        if (!ctx.isPropertyResolved()) {
+            ELSupport.throwUnhandled(t.base, property);
+        }
+        return ret;
+    }
+
+    @Override
+    public ValueReference getValueReference(EvaluationContext ctx) throws ELException {
+        Target t = getTarget(ctx);
+        if (t.isMethodCall()) {
+            return null;
+        }
+        Object property = t.suffixNode.getValue(ctx);
+        return new ValueReference(t.base, property);
+    }
+
+    private static AstMethodArguments getArguments(Node n) {
+        if (n instanceof AstDotSuffix && n.jjtGetNumChildren() > 0) {
+            return (AstMethodArguments) n.jjtGetChild(0);
+        }
+        if (n instanceof AstBracketSuffix && n.jjtGetNumChildren() > 1) {
+            return (AstMethodArguments) n.jjtGetChild(1);
+        }
+        return null;
+    }
+
+    private Object getValue(Object base, Node child, EvaluationContext ctx) throws ELException {
+
+        Object value = null;
+        ELResolver resolver = ctx.getELResolver();
+        Object property = child.getValue(ctx);
+        AstMethodArguments args = getArguments(child);
+        if (args != null) {
+            // This is a method call
+            if (!(property instanceof String)) {
+                throw new ELException(MessageFactory.get("error.method.name", property));
+            }
+            Class<?>[] paramTypes = args.getParamTypes();
+            Object[] params = args.getParameters(ctx);
+
+            ctx.setPropertyResolved(false);
+            value = resolver.invoke(ctx, base, property, paramTypes, params);
+        } else {
+            if (property != null) {
+                ctx.setPropertyResolved(false);
+                value = resolver.getValue(ctx, base, property);
+                if (!ctx.isPropertyResolved()) {
+                    ELSupport.throwUnhandled(base, property);
+                }
+            }
+        }
+        return value;
+    }
+
+    private Object getBase(EvaluationContext ctx) {
+        try {
+            return this.children[0].getValue(ctx);
+        } catch (PropertyNotFoundException ex) {
+            // Next check if the base is an imported class
+            if (this.children[0] instanceof AstIdentifier) {
+                String name = ((AstIdentifier) this.children[0]).image;
+                ImportHandler importHandler = ctx.getImportHandler();
+                if (importHandler != null) {
+                    Class<?> c = importHandler.resolveClass(name);
+                    if (c != null) {
+                        return new ELClass(c);
+                    }
+                }
+            }
+            throw ex;
+        }
+    }
+
+    private Target getTarget(EvaluationContext ctx) throws ELException {
+        // evaluate expr-a to value-a
+        Object base = getBase(ctx);
+
+        // if our base is null (we know there are more properites to evaluate)
+        if (base == null) {
+            throw new PropertyNotFoundException(MessageFactory.get("error.unreachable.base", this.children[0].getImage()));
+        }
+
+        // set up our start/end
+        Object property = null;
+        int propCount = this.jjtGetNumChildren() - 1;
+        int i = 1;
+
+        // evaluate any properties before our target
+        if (propCount > 1) {
+            while (base != null && i < propCount) {
+                base = getValue(base, this.children[i], ctx);
+                i++;
+            }
+            // if we are in this block, we have more properties to resolve,
+            // but our base was null
+            if (base == null) {
+                throw new PropertyNotFoundException(MessageFactory.get("error.unreachable.property", property));
+            }
+        }
+        return new Target(base, this.children[propCount]);
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        Object base = getBase(ctx);
+        int propCount = this.jjtGetNumChildren();
+        int i = 1;
+        while (base != null && i < propCount) {
+            base = getValue(base, this.children[i], ctx);
+            i++;
+        }
+        return base;
+    }
+
+    @Override
+    public boolean isReadOnly(EvaluationContext ctx) throws ELException {
+        Target t = getTarget(ctx);
+        if (t.isMethodCall()) {
+            return true;
+        }
+        Object property = t.suffixNode.getValue(ctx);
+        ctx.setPropertyResolved(false);
+        boolean ret = ctx.getELResolver().isReadOnly(ctx, t.base, property);
+        if (!ctx.isPropertyResolved()) {
+            ELSupport.throwUnhandled(t.base, property);
+        }
+        return ret;
+    }
+
+    @Override
+    public void setValue(EvaluationContext ctx, Object value) throws ELException {
+        Target t = getTarget(ctx);
+        if (t.isMethodCall()) {
+            throw new PropertyNotWritableException(MessageFactory.get("error.syntax.set"));
+        }
+        Object property = t.suffixNode.getValue(ctx);
+        ELResolver elResolver = ctx.getELResolver();
+
+        /*
+         * Note by kchung 10/2013 The spec does not say if the value should be cocerced to the target type before setting the
+         * value to the target. The conversion is kept here to be backward compatible.
+         */
+        ctx.setPropertyResolved(false);
+        Class<?> targetType = elResolver.getType(ctx, t.base, property);
+        if (ctx.isPropertyResolved()) {
+            ctx.setPropertyResolved(false);
+            Object targetValue = elResolver.convertToType(ctx, value, targetType);
+
+            if (ctx.isPropertyResolved()) {
+                value = targetValue;
+            } else {
+                if (value != null || targetType.isPrimitive()) {
+                    value = ELSupport.coerceToType(value, targetType);
+                }
+            }
+        }
+
+        ctx.setPropertyResolved(false);
+        elResolver.setValue(ctx, t.base, property, value);
+        if (!ctx.isPropertyResolved()) {
+            ELSupport.throwUnhandled(t.base, property);
+        }
+    }
+
+    @Override
+    public MethodInfo getMethodInfo(EvaluationContext ctx, Class[] paramTypes) throws ELException {
+        Target t = getTarget(ctx);
+        if (t.isMethodCall()) {
+            return null;
+        }
+        Object property = t.suffixNode.getValue(ctx);
+        Method m = ReflectionUtil.findMethod(t.base.getClass(), property.toString(), paramTypes, null);
+        return new MethodInfo(m.getName(), m.getReturnType(), m.getParameterTypes());
+    }
+
+    @Override
+    public Object invoke(EvaluationContext ctx, Class[] paramTypes, Object[] paramValues) throws ELException {
+        Target t = getTarget(ctx);
+        if (t.isMethodCall()) {
+            AstMethodArguments args = getArguments(t.suffixNode);
+            // Always use the param types in the expression, and ignore those
+            // specified elsewhere, such as TLD
+            paramTypes = args.getParamTypes();
+            Object[] params = args.getParameters(ctx);
+            String method = (String) t.suffixNode.getValue(ctx);
+
+            ctx.setPropertyResolved(false);
+            ELResolver resolver = ctx.getELResolver();
+            return resolver.invoke(ctx, t.base, method, paramTypes, params);
+        }
+        Object property = t.suffixNode.getValue(ctx);
+        Method m = ReflectionUtil.findMethod(t.base.getClass(), property.toString(), paramTypes, paramValues);
+        return ReflectionUtil.invokeMethod(ctx, m, t.base, paramValues);
+    }
+
+    @Override
+    public boolean isParametersProvided() {
+        return getArguments(this.children[this.jjtGetNumChildren() - 1]) != null;
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/BooleanNode.java b/impl/src/main/java/com/sun/el/parser/BooleanNode.java
new file mode 100644
index 0000000..5f4dec1
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/BooleanNode.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+import com.sun.el.lang.EvaluationContext;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public class BooleanNode extends SimpleNode {
+    /**
+     * @param i
+     */
+    public BooleanNode(int i) {
+        super(i);
+    }
+
+    @Override
+    public Class getType(EvaluationContext ctx) throws ELException {
+        return Boolean.class;
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/ELParser.java b/impl/src/main/java/com/sun/el/parser/ELParser.java
new file mode 100644
index 0000000..4d76641
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/ELParser.java
@@ -0,0 +1,3782 @@
+/*
+ * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/* Generated By:JJTree&JavaCC: Do not edit this line. ELParser.java */
+package com.sun.el.parser;
+
+import java.io.StringReader;
+
+import javax.el.ELException;
+
+public class ELParser/* @bgen(jjtree) */ implements ELParserTreeConstants, ELParserConstants {/* @bgen(jjtree) */
+    protected JJTELParserState jjtree = new JJTELParserState();
+
+    public static Node parse(String ref) throws ELException {
+        try {
+            return (new ELParser(new StringReader(ref))).CompositeExpression();
+        } catch (ParseException pe) {
+            throw new ELException(pe.getMessage());
+        }
+    }
+
+    /*
+     * CompositeExpression Allow most flexible parsing, restrict by examining type of returned node
+     */
+    final public AstCompositeExpression CompositeExpression() throws ParseException {
+        /* @bgen(jjtree) CompositeExpression */
+        AstCompositeExpression jjtn000 = new AstCompositeExpression(JJTCOMPOSITEEXPRESSION);
+        boolean jjtc000 = true;
+        jjtree.openNodeScope(jjtn000);
+        try {
+            label_1: while (true) {
+                switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+                case LITERAL_EXPRESSION:
+                case START_DYNAMIC_EXPRESSION:
+                case START_DEFERRED_EXPRESSION:
+                    ;
+                    break;
+                default:
+                    jj_la1[0] = jj_gen;
+                    break label_1;
+                }
+                switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+                case START_DEFERRED_EXPRESSION:
+                    DeferredExpression();
+                    break;
+                case START_DYNAMIC_EXPRESSION:
+                    DynamicExpression();
+                    break;
+                case LITERAL_EXPRESSION:
+                    LiteralExpression();
+                    break;
+                default:
+                    jj_la1[1] = jj_gen;
+                    jj_consume_token(-1);
+                    throw new ParseException();
+                }
+            }
+            jj_consume_token(0);
+            jjtree.closeNodeScope(jjtn000, true);
+            jjtc000 = false;
+            {
+                if (true) {
+                    return jjtn000;
+                }
+            }
+        } catch (Throwable jjte000) {
+            if (jjtc000) {
+                jjtree.clearNodeScope(jjtn000);
+                jjtc000 = false;
+            } else {
+                jjtree.popNode();
+            }
+            if (jjte000 instanceof RuntimeException) {
+                {
+                    if (true) {
+                        throw (RuntimeException) jjte000;
+                    }
+                }
+            }
+            if (jjte000 instanceof ParseException) {
+                {
+                    if (true) {
+                        throw (ParseException) jjte000;
+                    }
+                }
+            }
+            {
+                if (true) {
+                    throw (Error) jjte000;
+                }
+            }
+        } finally {
+            if (jjtc000) {
+                jjtree.closeNodeScope(jjtn000, true);
+            }
+        }
+        throw new Error("Missing return statement in function");
+    }
+
+    /*
+     * LiteralExpression Non-EL Expression blocks
+     */
+    final public void LiteralExpression() throws ParseException {
+        /* @bgen(jjtree) LiteralExpression */
+        AstLiteralExpression jjtn000 = new AstLiteralExpression(JJTLITERALEXPRESSION);
+        boolean jjtc000 = true;
+        jjtree.openNodeScope(jjtn000);
+        Token t = null;
+        try {
+            t = jj_consume_token(LITERAL_EXPRESSION);
+            jjtree.closeNodeScope(jjtn000, true);
+            jjtc000 = false;
+            jjtn000.setImage(t.image);
+        } finally {
+            if (jjtc000) {
+                jjtree.closeNodeScope(jjtn000, true);
+            }
+        }
+    }
+
+    /*
+     * DeferredExpression #{..} Expressions
+     */
+    final public void DeferredExpression() throws ParseException {
+        /* @bgen(jjtree) DeferredExpression */
+        AstDeferredExpression jjtn000 = new AstDeferredExpression(JJTDEFERREDEXPRESSION);
+        boolean jjtc000 = true;
+        jjtree.openNodeScope(jjtn000);
+        try {
+            jj_consume_token(START_DEFERRED_EXPRESSION);
+            Expression();
+            jj_consume_token(RCURL);
+        } catch (Throwable jjte000) {
+            if (jjtc000) {
+                jjtree.clearNodeScope(jjtn000);
+                jjtc000 = false;
+            } else {
+                jjtree.popNode();
+            }
+            if (jjte000 instanceof RuntimeException) {
+                {
+                    if (true) {
+                        throw (RuntimeException) jjte000;
+                    }
+                }
+            }
+            if (jjte000 instanceof ParseException) {
+                {
+                    if (true) {
+                        throw (ParseException) jjte000;
+                    }
+                }
+            }
+            {
+                if (true) {
+                    throw (Error) jjte000;
+                }
+            }
+        } finally {
+            if (jjtc000) {
+                jjtree.closeNodeScope(jjtn000, true);
+            }
+        }
+    }
+
+    /*
+     * DynamicExpression ${..} Expressions
+     */
+    final public void DynamicExpression() throws ParseException {
+        /* @bgen(jjtree) DynamicExpression */
+        AstDynamicExpression jjtn000 = new AstDynamicExpression(JJTDYNAMICEXPRESSION);
+        boolean jjtc000 = true;
+        jjtree.openNodeScope(jjtn000);
+        try {
+            jj_consume_token(START_DYNAMIC_EXPRESSION);
+            Expression();
+            jj_consume_token(RCURL);
+        } catch (Throwable jjte000) {
+            if (jjtc000) {
+                jjtree.clearNodeScope(jjtn000);
+                jjtc000 = false;
+            } else {
+                jjtree.popNode();
+            }
+            if (jjte000 instanceof RuntimeException) {
+                {
+                    if (true) {
+                        throw (RuntimeException) jjte000;
+                    }
+                }
+            }
+            if (jjte000 instanceof ParseException) {
+                {
+                    if (true) {
+                        throw (ParseException) jjte000;
+                    }
+                }
+            }
+            {
+                if (true) {
+                    throw (Error) jjte000;
+                }
+            }
+        } finally {
+            if (jjtc000) {
+                jjtree.closeNodeScope(jjtn000, true);
+            }
+        }
+    }
+
+    /*
+     * Jakarta Expression Root
+     */
+    final public void Expression() throws ParseException {
+        SemiColon();
+    }
+
+    /*
+     * SemiColon
+     */
+    final public void SemiColon() throws ParseException {
+        Assignment();
+        label_2: while (true) {
+            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+            case SEMICOLON:
+                ;
+                break;
+            default:
+                jj_la1[2] = jj_gen;
+                break label_2;
+            }
+            jj_consume_token(SEMICOLON);
+            AstSemiColon jjtn001 = new AstSemiColon(JJTSEMICOLON);
+            boolean jjtc001 = true;
+            jjtree.openNodeScope(jjtn001);
+            try {
+                Assignment();
+            } catch (Throwable jjte001) {
+                if (jjtc001) {
+                    jjtree.clearNodeScope(jjtn001);
+                    jjtc001 = false;
+                } else {
+                    jjtree.popNode();
+                }
+                if (jjte001 instanceof RuntimeException) {
+                    {
+                        if (true) {
+                            throw (RuntimeException) jjte001;
+                        }
+                    }
+                }
+                if (jjte001 instanceof ParseException) {
+                    {
+                        if (true) {
+                            throw (ParseException) jjte001;
+                        }
+                    }
+                }
+                {
+                    if (true) {
+                        throw (Error) jjte001;
+                    }
+                }
+            } finally {
+                if (jjtc001) {
+                    jjtree.closeNodeScope(jjtn001, 2);
+                }
+            }
+        }
+    }
+
+    /*
+     * Assignment For '=', right associatve, then LambdaExpression or Choice or Assignment
+     */
+    final public void Assignment() throws ParseException {
+        if (jj_2_1(3)) {
+            LambdaExpression();
+        } else {
+            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+            case START_MAP:
+            case INTEGER_LITERAL:
+            case FLOATING_POINT_LITERAL:
+            case STRING_LITERAL:
+            case TRUE:
+            case FALSE:
+            case NULL:
+            case LPAREN:
+            case LBRACK:
+            case NOT0:
+            case NOT1:
+            case EMPTY:
+            case MINUS:
+            case IDENTIFIER:
+                Choice();
+                switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+                case ASSIGN:
+                    jj_consume_token(ASSIGN);
+                    AstAssign jjtn001 = new AstAssign(JJTASSIGN);
+                    boolean jjtc001 = true;
+                    jjtree.openNodeScope(jjtn001);
+                    try {
+                        Assignment();
+                    } catch (Throwable jjte001) {
+                        if (jjtc001) {
+                            jjtree.clearNodeScope(jjtn001);
+                            jjtc001 = false;
+                        } else {
+                            jjtree.popNode();
+                        }
+                        if (jjte001 instanceof RuntimeException) {
+                            {
+                                if (true) {
+                                    throw (RuntimeException) jjte001;
+                                }
+                            }
+                        }
+                        if (jjte001 instanceof ParseException) {
+                            {
+                                if (true) {
+                                    throw (ParseException) jjte001;
+                                }
+                            }
+                        }
+                        {
+                            if (true) {
+                                throw (Error) jjte001;
+                            }
+                        }
+                    } finally {
+                        if (jjtc001) {
+                            jjtree.closeNodeScope(jjtn001, 2);
+                        }
+                    }
+                    break;
+                default:
+                    jj_la1[3] = jj_gen;
+                    ;
+                }
+                break;
+            default:
+                jj_la1[4] = jj_gen;
+                jj_consume_token(-1);
+                throw new ParseException();
+            }
+        }
+    }
+
+    /*
+     * LambdaExpression
+     */
+    final public void LambdaExpression() throws ParseException {
+        /* @bgen(jjtree) LambdaExpression */
+        AstLambdaExpression jjtn000 = new AstLambdaExpression(JJTLAMBDAEXPRESSION);
+        boolean jjtc000 = true;
+        jjtree.openNodeScope(jjtn000);
+        try {
+            LambdaParameters();
+            jj_consume_token(ARROW);
+            if (jj_2_2(3)) {
+                LambdaExpression();
+            } else {
+                switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+                case START_MAP:
+                case INTEGER_LITERAL:
+                case FLOATING_POINT_LITERAL:
+                case STRING_LITERAL:
+                case TRUE:
+                case FALSE:
+                case NULL:
+                case LPAREN:
+                case LBRACK:
+                case NOT0:
+                case NOT1:
+                case EMPTY:
+                case MINUS:
+                case IDENTIFIER:
+                    Choice();
+                    break;
+                default:
+                    jj_la1[5] = jj_gen;
+                    jj_consume_token(-1);
+                    throw new ParseException();
+                }
+            }
+        } catch (Throwable jjte000) {
+            if (jjtc000) {
+                jjtree.clearNodeScope(jjtn000);
+                jjtc000 = false;
+            } else {
+                jjtree.popNode();
+            }
+            if (jjte000 instanceof RuntimeException) {
+                {
+                    if (true) {
+                        throw (RuntimeException) jjte000;
+                    }
+                }
+            }
+            if (jjte000 instanceof ParseException) {
+                {
+                    if (true) {
+                        throw (ParseException) jjte000;
+                    }
+                }
+            }
+            {
+                if (true) {
+                    throw (Error) jjte000;
+                }
+            }
+        } finally {
+            if (jjtc000) {
+                jjtree.closeNodeScope(jjtn000, true);
+            }
+        }
+    }
+
+    final public void LambdaParameters() throws ParseException {
+        /* @bgen(jjtree) LambdaParameters */
+        AstLambdaParameters jjtn000 = new AstLambdaParameters(JJTLAMBDAPARAMETERS);
+        boolean jjtc000 = true;
+        jjtree.openNodeScope(jjtn000);
+        try {
+            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+            case IDENTIFIER:
+                Identifier();
+                break;
+            case LPAREN:
+                jj_consume_token(LPAREN);
+                switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+                case IDENTIFIER:
+                    Identifier();
+                    label_3: while (true) {
+                        switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+                        case COMMA:
+                            ;
+                            break;
+                        default:
+                            jj_la1[6] = jj_gen;
+                            break label_3;
+                        }
+                        jj_consume_token(COMMA);
+                        Identifier();
+                    }
+                    break;
+                default:
+                    jj_la1[7] = jj_gen;
+                    ;
+                }
+                jj_consume_token(RPAREN);
+                break;
+            default:
+                jj_la1[8] = jj_gen;
+                jj_consume_token(-1);
+                throw new ParseException();
+            }
+        } catch (Throwable jjte000) {
+            if (jjtc000) {
+                jjtree.clearNodeScope(jjtn000);
+                jjtc000 = false;
+            } else {
+                jjtree.popNode();
+            }
+            if (jjte000 instanceof RuntimeException) {
+                {
+                    if (true) {
+                        throw (RuntimeException) jjte000;
+                    }
+                }
+            }
+            if (jjte000 instanceof ParseException) {
+                {
+                    if (true) {
+                        throw (ParseException) jjte000;
+                    }
+                }
+            }
+            {
+                if (true) {
+                    throw (Error) jjte000;
+                }
+            }
+        } finally {
+            if (jjtc000) {
+                jjtree.closeNodeScope(jjtn000, true);
+            }
+        }
+    }
+
+    /*
+     * Choice For Choice markup a ? b : c, right associative
+     */
+    final public void Choice() throws ParseException {
+        Or();
+        switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+        case QUESTIONMARK:
+            jj_consume_token(QUESTIONMARK);
+            Choice();
+            jj_consume_token(COLON);
+            AstChoice jjtn001 = new AstChoice(JJTCHOICE);
+            boolean jjtc001 = true;
+            jjtree.openNodeScope(jjtn001);
+            try {
+                Choice();
+            } catch (Throwable jjte001) {
+                if (jjtc001) {
+                    jjtree.clearNodeScope(jjtn001);
+                    jjtc001 = false;
+                } else {
+                    jjtree.popNode();
+                }
+                if (jjte001 instanceof RuntimeException) {
+                    {
+                        if (true) {
+                            throw (RuntimeException) jjte001;
+                        }
+                    }
+                }
+                if (jjte001 instanceof ParseException) {
+                    {
+                        if (true) {
+                            throw (ParseException) jjte001;
+                        }
+                    }
+                }
+                {
+                    if (true) {
+                        throw (Error) jjte001;
+                    }
+                }
+            } finally {
+                if (jjtc001) {
+                    jjtree.closeNodeScope(jjtn001, 3);
+                }
+            }
+            break;
+        default:
+            jj_la1[9] = jj_gen;
+            ;
+        }
+    }
+
+    /*
+     * Or For 'or' '||', then And
+     */
+    final public void Or() throws ParseException {
+        And();
+        label_4: while (true) {
+            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+            case OR0:
+            case OR1:
+                ;
+                break;
+            default:
+                jj_la1[10] = jj_gen;
+                break label_4;
+            }
+            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+            case OR0:
+                jj_consume_token(OR0);
+                break;
+            case OR1:
+                jj_consume_token(OR1);
+                break;
+            default:
+                jj_la1[11] = jj_gen;
+                jj_consume_token(-1);
+                throw new ParseException();
+            }
+            AstOr jjtn001 = new AstOr(JJTOR);
+            boolean jjtc001 = true;
+            jjtree.openNodeScope(jjtn001);
+            try {
+                And();
+            } catch (Throwable jjte001) {
+                if (jjtc001) {
+                    jjtree.clearNodeScope(jjtn001);
+                    jjtc001 = false;
+                } else {
+                    jjtree.popNode();
+                }
+                if (jjte001 instanceof RuntimeException) {
+                    {
+                        if (true) {
+                            throw (RuntimeException) jjte001;
+                        }
+                    }
+                }
+                if (jjte001 instanceof ParseException) {
+                    {
+                        if (true) {
+                            throw (ParseException) jjte001;
+                        }
+                    }
+                }
+                {
+                    if (true) {
+                        throw (Error) jjte001;
+                    }
+                }
+            } finally {
+                if (jjtc001) {
+                    jjtree.closeNodeScope(jjtn001, 2);
+                }
+            }
+        }
+    }
+
+    /*
+     * And For 'and' '&&', then Equality
+     */
+    final public void And() throws ParseException {
+        Equality();
+        label_5: while (true) {
+            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+            case AND0:
+            case AND1:
+                ;
+                break;
+            default:
+                jj_la1[12] = jj_gen;
+                break label_5;
+            }
+            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+            case AND0:
+                jj_consume_token(AND0);
+                break;
+            case AND1:
+                jj_consume_token(AND1);
+                break;
+            default:
+                jj_la1[13] = jj_gen;
+                jj_consume_token(-1);
+                throw new ParseException();
+            }
+            AstAnd jjtn001 = new AstAnd(JJTAND);
+            boolean jjtc001 = true;
+            jjtree.openNodeScope(jjtn001);
+            try {
+                Equality();
+            } catch (Throwable jjte001) {
+                if (jjtc001) {
+                    jjtree.clearNodeScope(jjtn001);
+                    jjtc001 = false;
+                } else {
+                    jjtree.popNode();
+                }
+                if (jjte001 instanceof RuntimeException) {
+                    {
+                        if (true) {
+                            throw (RuntimeException) jjte001;
+                        }
+                    }
+                }
+                if (jjte001 instanceof ParseException) {
+                    {
+                        if (true) {
+                            throw (ParseException) jjte001;
+                        }
+                    }
+                }
+                {
+                    if (true) {
+                        throw (Error) jjte001;
+                    }
+                }
+            } finally {
+                if (jjtc001) {
+                    jjtree.closeNodeScope(jjtn001, 2);
+                }
+            }
+        }
+    }
+
+    /*
+     * Equality For '==' 'eq' '!=' 'ne', then Compare
+     */
+    final public void Equality() throws ParseException {
+        Compare();
+        label_6: while (true) {
+            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+            case EQ0:
+            case EQ1:
+            case NE0:
+            case NE1:
+                ;
+                break;
+            default:
+                jj_la1[14] = jj_gen;
+                break label_6;
+            }
+            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+            case EQ0:
+            case EQ1:
+                switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+                case EQ0:
+                    jj_consume_token(EQ0);
+                    break;
+                case EQ1:
+                    jj_consume_token(EQ1);
+                    break;
+                default:
+                    jj_la1[15] = jj_gen;
+                    jj_consume_token(-1);
+                    throw new ParseException();
+                }
+                AstEqual jjtn001 = new AstEqual(JJTEQUAL);
+                boolean jjtc001 = true;
+                jjtree.openNodeScope(jjtn001);
+                try {
+                    Compare();
+                } catch (Throwable jjte001) {
+                    if (jjtc001) {
+                        jjtree.clearNodeScope(jjtn001);
+                        jjtc001 = false;
+                    } else {
+                        jjtree.popNode();
+                    }
+                    if (jjte001 instanceof RuntimeException) {
+                        {
+                            if (true) {
+                                throw (RuntimeException) jjte001;
+                            }
+                        }
+                    }
+                    if (jjte001 instanceof ParseException) {
+                        {
+                            if (true) {
+                                throw (ParseException) jjte001;
+                            }
+                        }
+                    }
+                    {
+                        if (true) {
+                            throw (Error) jjte001;
+                        }
+                    }
+                } finally {
+                    if (jjtc001) {
+                        jjtree.closeNodeScope(jjtn001, 2);
+                    }
+                }
+                break;
+            case NE0:
+            case NE1:
+                switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+                case NE0:
+                    jj_consume_token(NE0);
+                    break;
+                case NE1:
+                    jj_consume_token(NE1);
+                    break;
+                default:
+                    jj_la1[16] = jj_gen;
+                    jj_consume_token(-1);
+                    throw new ParseException();
+                }
+                AstNotEqual jjtn002 = new AstNotEqual(JJTNOTEQUAL);
+                boolean jjtc002 = true;
+                jjtree.openNodeScope(jjtn002);
+                try {
+                    Compare();
+                } catch (Throwable jjte002) {
+                    if (jjtc002) {
+                        jjtree.clearNodeScope(jjtn002);
+                        jjtc002 = false;
+                    } else {
+                        jjtree.popNode();
+                    }
+                    if (jjte002 instanceof RuntimeException) {
+                        {
+                            if (true) {
+                                throw (RuntimeException) jjte002;
+                            }
+                        }
+                    }
+                    if (jjte002 instanceof ParseException) {
+                        {
+                            if (true) {
+                                throw (ParseException) jjte002;
+                            }
+                        }
+                    }
+                    {
+                        if (true) {
+                            throw (Error) jjte002;
+                        }
+                    }
+                } finally {
+                    if (jjtc002) {
+                        jjtree.closeNodeScope(jjtn002, 2);
+                    }
+                }
+                break;
+            default:
+                jj_la1[17] = jj_gen;
+                jj_consume_token(-1);
+                throw new ParseException();
+            }
+        }
+    }
+
+    /*
+     * Compare For a bunch of them, then Math
+     */
+    final public void Compare() throws ParseException {
+        Concatenation();
+        label_7: while (true) {
+            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+            case GT0:
+            case GT1:
+            case LT0:
+            case LT1:
+            case GE0:
+            case GE1:
+            case LE0:
+            case LE1:
+                ;
+                break;
+            default:
+                jj_la1[18] = jj_gen;
+                break label_7;
+            }
+            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+            case LT0:
+            case LT1:
+                switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+                case LT0:
+                    jj_consume_token(LT0);
+                    break;
+                case LT1:
+                    jj_consume_token(LT1);
+                    break;
+                default:
+                    jj_la1[19] = jj_gen;
+                    jj_consume_token(-1);
+                    throw new ParseException();
+                }
+                AstLessThan jjtn001 = new AstLessThan(JJTLESSTHAN);
+                boolean jjtc001 = true;
+                jjtree.openNodeScope(jjtn001);
+                try {
+                    Concatenation();
+                } catch (Throwable jjte001) {
+                    if (jjtc001) {
+                        jjtree.clearNodeScope(jjtn001);
+                        jjtc001 = false;
+                    } else {
+                        jjtree.popNode();
+                    }
+                    if (jjte001 instanceof RuntimeException) {
+                        {
+                            if (true) {
+                                throw (RuntimeException) jjte001;
+                            }
+                        }
+                    }
+                    if (jjte001 instanceof ParseException) {
+                        {
+                            if (true) {
+                                throw (ParseException) jjte001;
+                            }
+                        }
+                    }
+                    {
+                        if (true) {
+                            throw (Error) jjte001;
+                        }
+                    }
+                } finally {
+                    if (jjtc001) {
+                        jjtree.closeNodeScope(jjtn001, 2);
+                    }
+                }
+                break;
+            case GT0:
+            case GT1:
+                switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+                case GT0:
+                    jj_consume_token(GT0);
+                    break;
+                case GT1:
+                    jj_consume_token(GT1);
+                    break;
+                default:
+                    jj_la1[20] = jj_gen;
+                    jj_consume_token(-1);
+                    throw new ParseException();
+                }
+                AstGreaterThan jjtn002 = new AstGreaterThan(JJTGREATERTHAN);
+                boolean jjtc002 = true;
+                jjtree.openNodeScope(jjtn002);
+                try {
+                    Concatenation();
+                } catch (Throwable jjte002) {
+                    if (jjtc002) {
+                        jjtree.clearNodeScope(jjtn002);
+                        jjtc002 = false;
+                    } else {
+                        jjtree.popNode();
+                    }
+                    if (jjte002 instanceof RuntimeException) {
+                        {
+                            if (true) {
+                                throw (RuntimeException) jjte002;
+                            }
+                        }
+                    }
+                    if (jjte002 instanceof ParseException) {
+                        {
+                            if (true) {
+                                throw (ParseException) jjte002;
+                            }
+                        }
+                    }
+                    {
+                        if (true) {
+                            throw (Error) jjte002;
+                        }
+                    }
+                } finally {
+                    if (jjtc002) {
+                        jjtree.closeNodeScope(jjtn002, 2);
+                    }
+                }
+                break;
+            case LE0:
+            case LE1:
+                switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+                case LE0:
+                    jj_consume_token(LE0);
+                    break;
+                case LE1:
+                    jj_consume_token(LE1);
+                    break;
+                default:
+                    jj_la1[21] = jj_gen;
+                    jj_consume_token(-1);
+                    throw new ParseException();
+                }
+                AstLessThanEqual jjtn003 = new AstLessThanEqual(JJTLESSTHANEQUAL);
+                boolean jjtc003 = true;
+                jjtree.openNodeScope(jjtn003);
+                try {
+                    Concatenation();
+                } catch (Throwable jjte003) {
+                    if (jjtc003) {
+                        jjtree.clearNodeScope(jjtn003);
+                        jjtc003 = false;
+                    } else {
+                        jjtree.popNode();
+                    }
+                    if (jjte003 instanceof RuntimeException) {
+                        {
+                            if (true) {
+                                throw (RuntimeException) jjte003;
+                            }
+                        }
+                    }
+                    if (jjte003 instanceof ParseException) {
+                        {
+                            if (true) {
+                                throw (ParseException) jjte003;
+                            }
+                        }
+                    }
+                    {
+                        if (true) {
+                            throw (Error) jjte003;
+                        }
+                    }
+                } finally {
+                    if (jjtc003) {
+                        jjtree.closeNodeScope(jjtn003, 2);
+                    }
+                }
+                break;
+            case GE0:
+            case GE1:
+                switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+                case GE0:
+                    jj_consume_token(GE0);
+                    break;
+                case GE1:
+                    jj_consume_token(GE1);
+                    break;
+                default:
+                    jj_la1[22] = jj_gen;
+                    jj_consume_token(-1);
+                    throw new ParseException();
+                }
+                AstGreaterThanEqual jjtn004 = new AstGreaterThanEqual(JJTGREATERTHANEQUAL);
+                boolean jjtc004 = true;
+                jjtree.openNodeScope(jjtn004);
+                try {
+                    Concatenation();
+                } catch (Throwable jjte004) {
+                    if (jjtc004) {
+                        jjtree.clearNodeScope(jjtn004);
+                        jjtc004 = false;
+                    } else {
+                        jjtree.popNode();
+                    }
+                    if (jjte004 instanceof RuntimeException) {
+                        {
+                            if (true) {
+                                throw (RuntimeException) jjte004;
+                            }
+                        }
+                    }
+                    if (jjte004 instanceof ParseException) {
+                        {
+                            if (true) {
+                                throw (ParseException) jjte004;
+                            }
+                        }
+                    }
+                    {
+                        if (true) {
+                            throw (Error) jjte004;
+                        }
+                    }
+                } finally {
+                    if (jjtc004) {
+                        jjtree.closeNodeScope(jjtn004, 2);
+                    }
+                }
+                break;
+            default:
+                jj_la1[23] = jj_gen;
+                jj_consume_token(-1);
+                throw new ParseException();
+            }
+        }
+    }
+
+    /*
+     * Concatenation For '&', then Math()
+     */
+    final public void Concatenation() throws ParseException {
+        Math();
+        label_8: while (true) {
+            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+            case CONCAT:
+                ;
+                break;
+            default:
+                jj_la1[24] = jj_gen;
+                break label_8;
+            }
+            jj_consume_token(CONCAT);
+            AstConcat jjtn001 = new AstConcat(JJTCONCAT);
+            boolean jjtc001 = true;
+            jjtree.openNodeScope(jjtn001);
+            try {
+                Math();
+            } catch (Throwable jjte001) {
+                if (jjtc001) {
+                    jjtree.clearNodeScope(jjtn001);
+                    jjtc001 = false;
+                } else {
+                    jjtree.popNode();
+                }
+                if (jjte001 instanceof RuntimeException) {
+                    {
+                        if (true) {
+                            throw (RuntimeException) jjte001;
+                        }
+                    }
+                }
+                if (jjte001 instanceof ParseException) {
+                    {
+                        if (true) {
+                            throw (ParseException) jjte001;
+                        }
+                    }
+                }
+                {
+                    if (true) {
+                        throw (Error) jjte001;
+                    }
+                }
+            } finally {
+                if (jjtc001) {
+                    jjtree.closeNodeScope(jjtn001, 2);
+                }
+            }
+        }
+    }
+
+    /*
+     * Math For '+' '-', then Multiplication
+     */
+    final public void Math() throws ParseException {
+        Multiplication();
+        label_9: while (true) {
+            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+            case PLUS:
+            case MINUS:
+                ;
+                break;
+            default:
+                jj_la1[25] = jj_gen;
+                break label_9;
+            }
+            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+            case PLUS:
+                jj_consume_token(PLUS);
+                AstPlus jjtn001 = new AstPlus(JJTPLUS);
+                boolean jjtc001 = true;
+                jjtree.openNodeScope(jjtn001);
+                try {
+                    Multiplication();
+                } catch (Throwable jjte001) {
+                    if (jjtc001) {
+                        jjtree.clearNodeScope(jjtn001);
+                        jjtc001 = false;
+                    } else {
+                        jjtree.popNode();
+                    }
+                    if (jjte001 instanceof RuntimeException) {
+                        {
+                            if (true) {
+                                throw (RuntimeException) jjte001;
+                            }
+                        }
+                    }
+                    if (jjte001 instanceof ParseException) {
+                        {
+                            if (true) {
+                                throw (ParseException) jjte001;
+                            }
+                        }
+                    }
+                    {
+                        if (true) {
+                            throw (Error) jjte001;
+                        }
+                    }
+                } finally {
+                    if (jjtc001) {
+                        jjtree.closeNodeScope(jjtn001, 2);
+                    }
+                }
+                break;
+            case MINUS:
+                jj_consume_token(MINUS);
+                AstMinus jjtn002 = new AstMinus(JJTMINUS);
+                boolean jjtc002 = true;
+                jjtree.openNodeScope(jjtn002);
+                try {
+                    Multiplication();
+                } catch (Throwable jjte002) {
+                    if (jjtc002) {
+                        jjtree.clearNodeScope(jjtn002);
+                        jjtc002 = false;
+                    } else {
+                        jjtree.popNode();
+                    }
+                    if (jjte002 instanceof RuntimeException) {
+                        {
+                            if (true) {
+                                throw (RuntimeException) jjte002;
+                            }
+                        }
+                    }
+                    if (jjte002 instanceof ParseException) {
+                        {
+                            if (true) {
+                                throw (ParseException) jjte002;
+                            }
+                        }
+                    }
+                    {
+                        if (true) {
+                            throw (Error) jjte002;
+                        }
+                    }
+                } finally {
+                    if (jjtc002) {
+                        jjtree.closeNodeScope(jjtn002, 2);
+                    }
+                }
+                break;
+            default:
+                jj_la1[26] = jj_gen;
+                jj_consume_token(-1);
+                throw new ParseException();
+            }
+        }
+    }
+
+    /*
+     * Multiplication For a bunch of them, then Unary
+     */
+    final public void Multiplication() throws ParseException {
+        Unary();
+        label_10: while (true) {
+            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+            case MULT:
+            case DIV0:
+            case DIV1:
+            case MOD0:
+            case MOD1:
+                ;
+                break;
+            default:
+                jj_la1[27] = jj_gen;
+                break label_10;
+            }
+            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+            case MULT:
+                jj_consume_token(MULT);
+                AstMult jjtn001 = new AstMult(JJTMULT);
+                boolean jjtc001 = true;
+                jjtree.openNodeScope(jjtn001);
+                try {
+                    Unary();
+                } catch (Throwable jjte001) {
+                    if (jjtc001) {
+                        jjtree.clearNodeScope(jjtn001);
+                        jjtc001 = false;
+                    } else {
+                        jjtree.popNode();
+                    }
+                    if (jjte001 instanceof RuntimeException) {
+                        {
+                            if (true) {
+                                throw (RuntimeException) jjte001;
+                            }
+                        }
+                    }
+                    if (jjte001 instanceof ParseException) {
+                        {
+                            if (true) {
+                                throw (ParseException) jjte001;
+                            }
+                        }
+                    }
+                    {
+                        if (true) {
+                            throw (Error) jjte001;
+                        }
+                    }
+                } finally {
+                    if (jjtc001) {
+                        jjtree.closeNodeScope(jjtn001, 2);
+                    }
+                }
+                break;
+            case DIV0:
+            case DIV1:
+                switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+                case DIV0:
+                    jj_consume_token(DIV0);
+                    break;
+                case DIV1:
+                    jj_consume_token(DIV1);
+                    break;
+                default:
+                    jj_la1[28] = jj_gen;
+                    jj_consume_token(-1);
+                    throw new ParseException();
+                }
+                AstDiv jjtn002 = new AstDiv(JJTDIV);
+                boolean jjtc002 = true;
+                jjtree.openNodeScope(jjtn002);
+                try {
+                    Unary();
+                } catch (Throwable jjte002) {
+                    if (jjtc002) {
+                        jjtree.clearNodeScope(jjtn002);
+                        jjtc002 = false;
+                    } else {
+                        jjtree.popNode();
+                    }
+                    if (jjte002 instanceof RuntimeException) {
+                        {
+                            if (true) {
+                                throw (RuntimeException) jjte002;
+                            }
+                        }
+                    }
+                    if (jjte002 instanceof ParseException) {
+                        {
+                            if (true) {
+                                throw (ParseException) jjte002;
+                            }
+                        }
+                    }
+                    {
+                        if (true) {
+                            throw (Error) jjte002;
+                        }
+                    }
+                } finally {
+                    if (jjtc002) {
+                        jjtree.closeNodeScope(jjtn002, 2);
+                    }
+                }
+                break;
+            case MOD0:
+            case MOD1:
+                switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+                case MOD0:
+                    jj_consume_token(MOD0);
+                    break;
+                case MOD1:
+                    jj_consume_token(MOD1);
+                    break;
+                default:
+                    jj_la1[29] = jj_gen;
+                    jj_consume_token(-1);
+                    throw new ParseException();
+                }
+                AstMod jjtn003 = new AstMod(JJTMOD);
+                boolean jjtc003 = true;
+                jjtree.openNodeScope(jjtn003);
+                try {
+                    Unary();
+                } catch (Throwable jjte003) {
+                    if (jjtc003) {
+                        jjtree.clearNodeScope(jjtn003);
+                        jjtc003 = false;
+                    } else {
+                        jjtree.popNode();
+                    }
+                    if (jjte003 instanceof RuntimeException) {
+                        {
+                            if (true) {
+                                throw (RuntimeException) jjte003;
+                            }
+                        }
+                    }
+                    if (jjte003 instanceof ParseException) {
+                        {
+                            if (true) {
+                                throw (ParseException) jjte003;
+                            }
+                        }
+                    }
+                    {
+                        if (true) {
+                            throw (Error) jjte003;
+                        }
+                    }
+                } finally {
+                    if (jjtc003) {
+                        jjtree.closeNodeScope(jjtn003, 2);
+                    }
+                }
+                break;
+            default:
+                jj_la1[30] = jj_gen;
+                jj_consume_token(-1);
+                throw new ParseException();
+            }
+        }
+    }
+
+    /*
+     * Unary For '-' '!' 'not' 'empty', then Value
+     */
+    final public void Unary() throws ParseException {
+        switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+        case MINUS:
+            jj_consume_token(MINUS);
+            AstNegative jjtn001 = new AstNegative(JJTNEGATIVE);
+            boolean jjtc001 = true;
+            jjtree.openNodeScope(jjtn001);
+            try {
+                Unary();
+            } catch (Throwable jjte001) {
+                if (jjtc001) {
+                    jjtree.clearNodeScope(jjtn001);
+                    jjtc001 = false;
+                } else {
+                    jjtree.popNode();
+                }
+                if (jjte001 instanceof RuntimeException) {
+                    {
+                        if (true) {
+                            throw (RuntimeException) jjte001;
+                        }
+                    }
+                }
+                if (jjte001 instanceof ParseException) {
+                    {
+                        if (true) {
+                            throw (ParseException) jjte001;
+                        }
+                    }
+                }
+                {
+                    if (true) {
+                        throw (Error) jjte001;
+                    }
+                }
+            } finally {
+                if (jjtc001) {
+                    jjtree.closeNodeScope(jjtn001, true);
+                }
+            }
+            break;
+        case NOT0:
+        case NOT1:
+            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+            case NOT0:
+                jj_consume_token(NOT0);
+                break;
+            case NOT1:
+                jj_consume_token(NOT1);
+                break;
+            default:
+                jj_la1[31] = jj_gen;
+                jj_consume_token(-1);
+                throw new ParseException();
+            }
+            AstNot jjtn002 = new AstNot(JJTNOT);
+            boolean jjtc002 = true;
+            jjtree.openNodeScope(jjtn002);
+            try {
+                Unary();
+            } catch (Throwable jjte002) {
+                if (jjtc002) {
+                    jjtree.clearNodeScope(jjtn002);
+                    jjtc002 = false;
+                } else {
+                    jjtree.popNode();
+                }
+                if (jjte002 instanceof RuntimeException) {
+                    {
+                        if (true) {
+                            throw (RuntimeException) jjte002;
+                        }
+                    }
+                }
+                if (jjte002 instanceof ParseException) {
+                    {
+                        if (true) {
+                            throw (ParseException) jjte002;
+                        }
+                    }
+                }
+                {
+                    if (true) {
+                        throw (Error) jjte002;
+                    }
+                }
+            } finally {
+                if (jjtc002) {
+                    jjtree.closeNodeScope(jjtn002, true);
+                }
+            }
+            break;
+        case EMPTY:
+            jj_consume_token(EMPTY);
+            AstEmpty jjtn003 = new AstEmpty(JJTEMPTY);
+            boolean jjtc003 = true;
+            jjtree.openNodeScope(jjtn003);
+            try {
+                Unary();
+            } catch (Throwable jjte003) {
+                if (jjtc003) {
+                    jjtree.clearNodeScope(jjtn003);
+                    jjtc003 = false;
+                } else {
+                    jjtree.popNode();
+                }
+                if (jjte003 instanceof RuntimeException) {
+                    {
+                        if (true) {
+                            throw (RuntimeException) jjte003;
+                        }
+                    }
+                }
+                if (jjte003 instanceof ParseException) {
+                    {
+                        if (true) {
+                            throw (ParseException) jjte003;
+                        }
+                    }
+                }
+                {
+                    if (true) {
+                        throw (Error) jjte003;
+                    }
+                }
+            } finally {
+                if (jjtc003) {
+                    jjtree.closeNodeScope(jjtn003, true);
+                }
+            }
+            break;
+        case START_MAP:
+        case INTEGER_LITERAL:
+        case FLOATING_POINT_LITERAL:
+        case STRING_LITERAL:
+        case TRUE:
+        case FALSE:
+        case NULL:
+        case LPAREN:
+        case LBRACK:
+        case IDENTIFIER:
+            Value();
+            break;
+        default:
+            jj_la1[32] = jj_gen;
+            jj_consume_token(-1);
+            throw new ParseException();
+        }
+    }
+
+    /*
+     * Value Defines Prefix plus zero or more Suffixes
+     */
+    final public void Value() throws ParseException {
+        AstValue jjtn001 = new AstValue(JJTVALUE);
+        boolean jjtc001 = true;
+        jjtree.openNodeScope(jjtn001);
+        try {
+            ValuePrefix();
+            label_11: while (true) {
+                switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+                case DOT:
+                case LBRACK:
+                    ;
+                    break;
+                default:
+                    jj_la1[33] = jj_gen;
+                    break label_11;
+                }
+                ValueSuffix();
+            }
+        } catch (Throwable jjte001) {
+            if (jjtc001) {
+                jjtree.clearNodeScope(jjtn001);
+                jjtc001 = false;
+            } else {
+                jjtree.popNode();
+            }
+            if (jjte001 instanceof RuntimeException) {
+                {
+                    if (true) {
+                        throw (RuntimeException) jjte001;
+                    }
+                }
+            }
+            if (jjte001 instanceof ParseException) {
+                {
+                    if (true) {
+                        throw (ParseException) jjte001;
+                    }
+                }
+            }
+            {
+                if (true) {
+                    throw (Error) jjte001;
+                }
+            }
+        } finally {
+            if (jjtc001) {
+                jjtree.closeNodeScope(jjtn001, jjtree.nodeArity() > 1);
+            }
+        }
+    }
+
+    /*
+     * ValuePrefix For Literals, Variables, and Functions
+     */
+    final public void ValuePrefix() throws ParseException {
+        switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+        case INTEGER_LITERAL:
+        case FLOATING_POINT_LITERAL:
+        case STRING_LITERAL:
+        case TRUE:
+        case FALSE:
+        case NULL:
+            Literal();
+            break;
+        case START_MAP:
+        case LPAREN:
+        case LBRACK:
+        case IDENTIFIER:
+            NonLiteral();
+            break;
+        default:
+            jj_la1[34] = jj_gen;
+            jj_consume_token(-1);
+            throw new ParseException();
+        }
+    }
+
+    /*
+     * ValueSuffix Either dot or bracket notation
+     */
+    final public void ValueSuffix() throws ParseException {
+        switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+        case DOT:
+            DotSuffix();
+            break;
+        case LBRACK:
+            BracketSuffix();
+            break;
+        default:
+            jj_la1[35] = jj_gen;
+            jj_consume_token(-1);
+            throw new ParseException();
+        }
+    }
+
+    /*
+     * DotSuffix Dot Property and Dot Method
+     */
+    final public void DotSuffix() throws ParseException {
+        /* @bgen(jjtree) DotSuffix */
+        AstDotSuffix jjtn000 = new AstDotSuffix(JJTDOTSUFFIX);
+        boolean jjtc000 = true;
+        jjtree.openNodeScope(jjtn000);
+        Token t = null;
+        try {
+            jj_consume_token(DOT);
+            t = jj_consume_token(IDENTIFIER);
+            jjtn000.setImage(t.image);
+            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+            case LPAREN:
+                MethodArguments();
+                break;
+            default:
+                jj_la1[36] = jj_gen;
+                ;
+            }
+        } catch (Throwable jjte000) {
+            if (jjtc000) {
+                jjtree.clearNodeScope(jjtn000);
+                jjtc000 = false;
+            } else {
+                jjtree.popNode();
+            }
+            if (jjte000 instanceof RuntimeException) {
+                {
+                    if (true) {
+                        throw (RuntimeException) jjte000;
+                    }
+                }
+            }
+            if (jjte000 instanceof ParseException) {
+                {
+                    if (true) {
+                        throw (ParseException) jjte000;
+                    }
+                }
+            }
+            {
+                if (true) {
+                    throw (Error) jjte000;
+                }
+            }
+        } finally {
+            if (jjtc000) {
+                jjtree.closeNodeScope(jjtn000, true);
+            }
+        }
+    }
+
+    /*
+     * BracketSuffix Sub Expression Suffix
+     */
+    final public void BracketSuffix() throws ParseException {
+        /* @bgen(jjtree) BracketSuffix */
+        AstBracketSuffix jjtn000 = new AstBracketSuffix(JJTBRACKETSUFFIX);
+        boolean jjtc000 = true;
+        jjtree.openNodeScope(jjtn000);
+        try {
+            jj_consume_token(LBRACK);
+            Expression();
+            jj_consume_token(RBRACK);
+            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+            case LPAREN:
+                MethodArguments();
+                break;
+            default:
+                jj_la1[37] = jj_gen;
+                ;
+            }
+        } catch (Throwable jjte000) {
+            if (jjtc000) {
+                jjtree.clearNodeScope(jjtn000);
+                jjtc000 = false;
+            } else {
+                jjtree.popNode();
+            }
+            if (jjte000 instanceof RuntimeException) {
+                {
+                    if (true) {
+                        throw (RuntimeException) jjte000;
+                    }
+                }
+            }
+            if (jjte000 instanceof ParseException) {
+                {
+                    if (true) {
+                        throw (ParseException) jjte000;
+                    }
+                }
+            }
+            {
+                if (true) {
+                    throw (Error) jjte000;
+                }
+            }
+        } finally {
+            if (jjtc000) {
+                jjtree.closeNodeScope(jjtn000, true);
+            }
+        }
+    }
+
+    /*
+     * MethodArguments
+     */
+    final public void MethodArguments() throws ParseException {
+        /* @bgen(jjtree) MethodArguments */
+        AstMethodArguments jjtn000 = new AstMethodArguments(JJTMETHODARGUMENTS);
+        boolean jjtc000 = true;
+        jjtree.openNodeScope(jjtn000);
+        try {
+            jj_consume_token(LPAREN);
+            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+            case START_MAP:
+            case INTEGER_LITERAL:
+            case FLOATING_POINT_LITERAL:
+            case STRING_LITERAL:
+            case TRUE:
+            case FALSE:
+            case NULL:
+            case LPAREN:
+            case LBRACK:
+            case NOT0:
+            case NOT1:
+            case EMPTY:
+            case MINUS:
+            case IDENTIFIER:
+                Expression();
+                label_12: while (true) {
+                    switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+                    case COMMA:
+                        ;
+                        break;
+                    default:
+                        jj_la1[38] = jj_gen;
+                        break label_12;
+                    }
+                    jj_consume_token(COMMA);
+                    Expression();
+                }
+                break;
+            default:
+                jj_la1[39] = jj_gen;
+                ;
+            }
+            jj_consume_token(RPAREN);
+        } catch (Throwable jjte000) {
+            if (jjtc000) {
+                jjtree.clearNodeScope(jjtn000);
+                jjtc000 = false;
+            } else {
+                jjtree.popNode();
+            }
+            if (jjte000 instanceof RuntimeException) {
+                {
+                    if (true) {
+                        throw (RuntimeException) jjte000;
+                    }
+                }
+            }
+            if (jjte000 instanceof ParseException) {
+                {
+                    if (true) {
+                        throw (ParseException) jjte000;
+                    }
+                }
+            }
+            {
+                if (true) {
+                    throw (Error) jjte000;
+                }
+            }
+        } finally {
+            if (jjtc000) {
+                jjtree.closeNodeScope(jjtn000, true);
+            }
+        }
+    }
+
+    /*
+     * Parenthesized Lambda Expression, with optional invokation
+     */
+    final public void LambdaExpressionOrCall() throws ParseException {
+        /* @bgen(jjtree) LambdaExpression */
+        AstLambdaExpression jjtn000 = new AstLambdaExpression(JJTLAMBDAEXPRESSION);
+        boolean jjtc000 = true;
+        jjtree.openNodeScope(jjtn000);
+        try {
+            jj_consume_token(LPAREN);
+            LambdaParameters();
+            jj_consume_token(ARROW);
+            if (jj_2_3(3)) {
+                LambdaExpression();
+            } else {
+                switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+                case START_MAP:
+                case INTEGER_LITERAL:
+                case FLOATING_POINT_LITERAL:
+                case STRING_LITERAL:
+                case TRUE:
+                case FALSE:
+                case NULL:
+                case LPAREN:
+                case LBRACK:
+                case NOT0:
+                case NOT1:
+                case EMPTY:
+                case MINUS:
+                case IDENTIFIER:
+                    Choice();
+                    break;
+                default:
+                    jj_la1[40] = jj_gen;
+                    jj_consume_token(-1);
+                    throw new ParseException();
+                }
+            }
+            jj_consume_token(RPAREN);
+            label_13: while (true) {
+                switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+                case LPAREN:
+                    ;
+                    break;
+                default:
+                    jj_la1[41] = jj_gen;
+                    break label_13;
+                }
+                MethodArguments();
+            }
+        } catch (Throwable jjte000) {
+            if (jjtc000) {
+                jjtree.clearNodeScope(jjtn000);
+                jjtc000 = false;
+            } else {
+                jjtree.popNode();
+            }
+            if (jjte000 instanceof RuntimeException) {
+                {
+                    if (true) {
+                        throw (RuntimeException) jjte000;
+                    }
+                }
+            }
+            if (jjte000 instanceof ParseException) {
+                {
+                    if (true) {
+                        throw (ParseException) jjte000;
+                    }
+                }
+            }
+            {
+                if (true) {
+                    throw (Error) jjte000;
+                }
+            }
+        } finally {
+            if (jjtc000) {
+                jjtree.closeNodeScope(jjtn000, true);
+            }
+        }
+    }
+
+    /*
+     * NonLiteral For Grouped Operations, Identifiers, and Functions
+     */
+    final public void NonLiteral() throws ParseException {
+        if (jj_2_4(4)) {
+            LambdaExpressionOrCall();
+        } else {
+            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+            case LPAREN:
+                jj_consume_token(LPAREN);
+                Expression();
+                jj_consume_token(RPAREN);
+                break;
+            default:
+                jj_la1[42] = jj_gen;
+                if (jj_2_5(4)) {
+                    Function();
+                } else {
+                    switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+                    case IDENTIFIER:
+                        Identifier();
+                        break;
+                    case START_MAP:
+                        MapData();
+                        break;
+                    case LBRACK:
+                        ListData();
+                        break;
+                    default:
+                        jj_la1[43] = jj_gen;
+                        jj_consume_token(-1);
+                        throw new ParseException();
+                    }
+                }
+            }
+        }
+    }
+
+    final public void MapData() throws ParseException {
+        /* @bgen(jjtree) MapData */
+        AstMapData jjtn000 = new AstMapData(JJTMAPDATA);
+        boolean jjtc000 = true;
+        jjtree.openNodeScope(jjtn000);
+        try {
+            jj_consume_token(START_MAP);
+            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+            case START_MAP:
+            case INTEGER_LITERAL:
+            case FLOATING_POINT_LITERAL:
+            case STRING_LITERAL:
+            case TRUE:
+            case FALSE:
+            case NULL:
+            case LPAREN:
+            case LBRACK:
+            case NOT0:
+            case NOT1:
+            case EMPTY:
+            case MINUS:
+            case IDENTIFIER:
+                MapEntry();
+                label_14: while (true) {
+                    switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+                    case COMMA:
+                        ;
+                        break;
+                    default:
+                        jj_la1[44] = jj_gen;
+                        break label_14;
+                    }
+                    jj_consume_token(COMMA);
+                    MapEntry();
+                }
+                break;
+            default:
+                jj_la1[45] = jj_gen;
+                ;
+            }
+            jj_consume_token(RCURL);
+        } catch (Throwable jjte000) {
+            if (jjtc000) {
+                jjtree.clearNodeScope(jjtn000);
+                jjtc000 = false;
+            } else {
+                jjtree.popNode();
+            }
+            if (jjte000 instanceof RuntimeException) {
+                {
+                    if (true) {
+                        throw (RuntimeException) jjte000;
+                    }
+                }
+            }
+            if (jjte000 instanceof ParseException) {
+                {
+                    if (true) {
+                        throw (ParseException) jjte000;
+                    }
+                }
+            }
+            {
+                if (true) {
+                    throw (Error) jjte000;
+                }
+            }
+        } finally {
+            if (jjtc000) {
+                jjtree.closeNodeScope(jjtn000, true);
+            }
+        }
+    }
+
+    final public void MapEntry() throws ParseException {
+        /* @bgen(jjtree) MapEntry */
+        AstMapEntry jjtn000 = new AstMapEntry(JJTMAPENTRY);
+        boolean jjtc000 = true;
+        jjtree.openNodeScope(jjtn000);
+        try {
+            Expression();
+            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+            case COLON:
+                jj_consume_token(COLON);
+                Expression();
+                break;
+            default:
+                jj_la1[46] = jj_gen;
+                ;
+            }
+        } catch (Throwable jjte000) {
+            if (jjtc000) {
+                jjtree.clearNodeScope(jjtn000);
+                jjtc000 = false;
+            } else {
+                jjtree.popNode();
+            }
+            if (jjte000 instanceof RuntimeException) {
+                {
+                    if (true) {
+                        throw (RuntimeException) jjte000;
+                    }
+                }
+            }
+            if (jjte000 instanceof ParseException) {
+                {
+                    if (true) {
+                        throw (ParseException) jjte000;
+                    }
+                }
+            }
+            {
+                if (true) {
+                    throw (Error) jjte000;
+                }
+            }
+        } finally {
+            if (jjtc000) {
+                jjtree.closeNodeScope(jjtn000, true);
+            }
+        }
+    }
+
+    final public void ListData() throws ParseException {
+        /* @bgen(jjtree) ListData */
+        AstListData jjtn000 = new AstListData(JJTLISTDATA);
+        boolean jjtc000 = true;
+        jjtree.openNodeScope(jjtn000);
+        try {
+            jj_consume_token(LBRACK);
+            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+            case START_MAP:
+            case INTEGER_LITERAL:
+            case FLOATING_POINT_LITERAL:
+            case STRING_LITERAL:
+            case TRUE:
+            case FALSE:
+            case NULL:
+            case LPAREN:
+            case LBRACK:
+            case NOT0:
+            case NOT1:
+            case EMPTY:
+            case MINUS:
+            case IDENTIFIER:
+                Expression();
+                label_15: while (true) {
+                    switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+                    case COMMA:
+                        ;
+                        break;
+                    default:
+                        jj_la1[47] = jj_gen;
+                        break label_15;
+                    }
+                    jj_consume_token(COMMA);
+                    Expression();
+                }
+                break;
+            default:
+                jj_la1[48] = jj_gen;
+                ;
+            }
+            jj_consume_token(RBRACK);
+        } catch (Throwable jjte000) {
+            if (jjtc000) {
+                jjtree.clearNodeScope(jjtn000);
+                jjtc000 = false;
+            } else {
+                jjtree.popNode();
+            }
+            if (jjte000 instanceof RuntimeException) {
+                {
+                    if (true) {
+                        throw (RuntimeException) jjte000;
+                    }
+                }
+            }
+            if (jjte000 instanceof ParseException) {
+                {
+                    if (true) {
+                        throw (ParseException) jjte000;
+                    }
+                }
+            }
+            {
+                if (true) {
+                    throw (Error) jjte000;
+                }
+            }
+        } finally {
+            if (jjtc000) {
+                jjtree.closeNodeScope(jjtn000, true);
+            }
+        }
+    }
+
+    /*
+     * Identifier Java Language Identifier
+     */
+    final public void Identifier() throws ParseException {
+        /* @bgen(jjtree) Identifier */
+        AstIdentifier jjtn000 = new AstIdentifier(JJTIDENTIFIER);
+        boolean jjtc000 = true;
+        jjtree.openNodeScope(jjtn000);
+        Token t = null;
+        try {
+            t = jj_consume_token(IDENTIFIER);
+            jjtree.closeNodeScope(jjtn000, true);
+            jjtc000 = false;
+            jjtn000.setImage(t.image);
+        } finally {
+            if (jjtc000) {
+                jjtree.closeNodeScope(jjtn000, true);
+            }
+        }
+    }
+
+    /*
+     * Function Namespace:Name(a,b,c)
+     */
+    final public void Function() throws ParseException {
+        /* @bgen(jjtree) Function */
+        AstFunction jjtn000 = new AstFunction(JJTFUNCTION);
+        boolean jjtc000 = true;
+        jjtree.openNodeScope(jjtn000);
+        Token t0 = null;
+        Token t1 = null;
+        try {
+            t0 = jj_consume_token(IDENTIFIER);
+            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+            case COLON:
+                jj_consume_token(COLON);
+                t1 = jj_consume_token(IDENTIFIER);
+                break;
+            default:
+                jj_la1[49] = jj_gen;
+                ;
+            }
+            if (t1 != null) {
+                jjtn000.setPrefix(t0.image);
+                jjtn000.setLocalName(t1.image);
+            } else {
+                jjtn000.setLocalName(t0.image);
+            }
+            label_16: while (true) {
+                MethodArguments();
+                switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+                case LPAREN:
+                    ;
+                    break;
+                default:
+                    jj_la1[50] = jj_gen;
+                    break label_16;
+                }
+            }
+        } catch (Throwable jjte000) {
+            if (jjtc000) {
+                jjtree.clearNodeScope(jjtn000);
+                jjtc000 = false;
+            } else {
+                jjtree.popNode();
+            }
+            if (jjte000 instanceof RuntimeException) {
+                {
+                    if (true) {
+                        throw (RuntimeException) jjte000;
+                    }
+                }
+            }
+            if (jjte000 instanceof ParseException) {
+                {
+                    if (true) {
+                        throw (ParseException) jjte000;
+                    }
+                }
+            }
+            {
+                if (true) {
+                    throw (Error) jjte000;
+                }
+            }
+        } finally {
+            if (jjtc000) {
+                jjtree.closeNodeScope(jjtn000, true);
+            }
+        }
+    }
+
+    /*
+     * Literal Reserved Keywords
+     */
+    final public void Literal() throws ParseException {
+        switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+        case TRUE:
+        case FALSE:
+            Boolean();
+            break;
+        case FLOATING_POINT_LITERAL:
+            FloatingPoint();
+            break;
+        case INTEGER_LITERAL:
+            Integer();
+            break;
+        case STRING_LITERAL:
+            String();
+            break;
+        case NULL:
+            Null();
+            break;
+        default:
+            jj_la1[51] = jj_gen;
+            jj_consume_token(-1);
+            throw new ParseException();
+        }
+    }
+
+    /*
+     * Boolean For 'true' 'false'
+     */
+    final public void Boolean() throws ParseException {
+        switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
+        case TRUE:
+            AstTrue jjtn001 = new AstTrue(JJTTRUE);
+            boolean jjtc001 = true;
+            jjtree.openNodeScope(jjtn001);
+            try {
+                jj_consume_token(TRUE);
+            } finally {
+                if (jjtc001) {
+                    jjtree.closeNodeScope(jjtn001, true);
+                }
+            }
+            break;
+        case FALSE:
+            AstFalse jjtn002 = new AstFalse(JJTFALSE);
+            boolean jjtc002 = true;
+            jjtree.openNodeScope(jjtn002);
+            try {
+                jj_consume_token(FALSE);
+            } finally {
+                if (jjtc002) {
+                    jjtree.closeNodeScope(jjtn002, true);
+                }
+            }
+            break;
+        default:
+            jj_la1[52] = jj_gen;
+            jj_consume_token(-1);
+            throw new ParseException();
+        }
+    }
+
+    /*
+     * FloatinPoint For Decimal and Floating Point Literals
+     */
+    final public void FloatingPoint() throws ParseException {
+        /* @bgen(jjtree) FloatingPoint */
+        AstFloatingPoint jjtn000 = new AstFloatingPoint(JJTFLOATINGPOINT);
+        boolean jjtc000 = true;
+        jjtree.openNodeScope(jjtn000);
+        Token t = null;
+        try {
+            t = jj_consume_token(FLOATING_POINT_LITERAL);
+            jjtree.closeNodeScope(jjtn000, true);
+            jjtc000 = false;
+            jjtn000.setImage(t.image);
+        } finally {
+            if (jjtc000) {
+                jjtree.closeNodeScope(jjtn000, true);
+            }
+        }
+    }
+
+    /*
+     * Integer For Simple Numeric Literals
+     */
+    final public void Integer() throws ParseException {
+        /* @bgen(jjtree) Integer */
+        AstInteger jjtn000 = new AstInteger(JJTINTEGER);
+        boolean jjtc000 = true;
+        jjtree.openNodeScope(jjtn000);
+        Token t = null;
+        try {
+            t = jj_consume_token(INTEGER_LITERAL);
+            jjtree.closeNodeScope(jjtn000, true);
+            jjtc000 = false;
+            jjtn000.setImage(t.image);
+        } finally {
+            if (jjtc000) {
+                jjtree.closeNodeScope(jjtn000, true);
+            }
+        }
+    }
+
+    /*
+     * String For Quoted Literals
+     */
+    final public void String() throws ParseException {
+        /* @bgen(jjtree) String */
+        AstString jjtn000 = new AstString(JJTSTRING);
+        boolean jjtc000 = true;
+        jjtree.openNodeScope(jjtn000);
+        Token t = null;
+        try {
+            t = jj_consume_token(STRING_LITERAL);
+            jjtree.closeNodeScope(jjtn000, true);
+            jjtc000 = false;
+            jjtn000.setImage(t.image);
+        } finally {
+            if (jjtc000) {
+                jjtree.closeNodeScope(jjtn000, true);
+            }
+        }
+    }
+
+    /*
+     * Null For 'null'
+     */
+    final public void Null() throws ParseException {
+        /* @bgen(jjtree) Null */
+        AstNull jjtn000 = new AstNull(JJTNULL);
+        boolean jjtc000 = true;
+        jjtree.openNodeScope(jjtn000);
+        try {
+            jj_consume_token(NULL);
+        } finally {
+            if (jjtc000) {
+                jjtree.closeNodeScope(jjtn000, true);
+            }
+        }
+    }
+
+    private boolean jj_2_1(int xla) {
+        jj_la = xla;
+        jj_lastpos = jj_scanpos = token;
+        try {
+            return !jj_3_1();
+        } catch (LookaheadSuccess ls) {
+            return true;
+        } finally {
+            jj_save(0, xla);
+        }
+    }
+
+    private boolean jj_2_2(int xla) {
+        jj_la = xla;
+        jj_lastpos = jj_scanpos = token;
+        try {
+            return !jj_3_2();
+        } catch (LookaheadSuccess ls) {
+            return true;
+        } finally {
+            jj_save(1, xla);
+        }
+    }
+
+    private boolean jj_2_3(int xla) {
+        jj_la = xla;
+        jj_lastpos = jj_scanpos = token;
+        try {
+            return !jj_3_3();
+        } catch (LookaheadSuccess ls) {
+            return true;
+        } finally {
+            jj_save(2, xla);
+        }
+    }
+
+    private boolean jj_2_4(int xla) {
+        jj_la = xla;
+        jj_lastpos = jj_scanpos = token;
+        try {
+            return !jj_3_4();
+        } catch (LookaheadSuccess ls) {
+            return true;
+        } finally {
+            jj_save(3, xla);
+        }
+    }
+
+    private boolean jj_2_5(int xla) {
+        jj_la = xla;
+        jj_lastpos = jj_scanpos = token;
+        try {
+            return !jj_3_5();
+        } catch (LookaheadSuccess ls) {
+            return true;
+        } finally {
+            jj_save(4, xla);
+        }
+    }
+
+    private boolean jj_3R_89() {
+        if (jj_scan_token(LBRACK)) {
+            return true;
+        }
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_3R_102()) {
+            jj_scanpos = xsp;
+        }
+        if (jj_scan_token(RBRACK)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_33() {
+        if (jj_scan_token(COMMA)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_31() {
+        if (jj_3R_34()) {
+            return true;
+        }
+        Token xsp;
+        while (true) {
+            xsp = jj_scanpos;
+            if (jj_3R_49()) {
+                jj_scanpos = xsp;
+                break;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_47() {
+        if (jj_scan_token(QUESTIONMARK)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_103() {
+        if (jj_3R_35()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_101() {
+        if (jj_3R_103()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_27() {
+        if (jj_3R_31()) {
+            return true;
+        }
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_3R_47()) {
+            jj_scanpos = xsp;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_79() {
+        if (jj_3R_89()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_88() {
+        if (jj_scan_token(START_MAP)) {
+            return true;
+        }
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_3R_101()) {
+            jj_scanpos = xsp;
+        }
+        if (jj_scan_token(RCURL)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_78() {
+        if (jj_3R_88()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_77() {
+        if (jj_3R_29()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3_5() {
+        if (jj_3R_19()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_30() {
+        if (jj_3R_29()) {
+            return true;
+        }
+        Token xsp;
+        while (true) {
+            xsp = jj_scanpos;
+            if (jj_3R_33()) {
+                jj_scanpos = xsp;
+                break;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_76() {
+        if (jj_scan_token(LPAREN)) {
+            return true;
+        }
+        if (jj_3R_35()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_36() {
+        if (jj_scan_token(COMMA)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3_4() {
+        if (jj_3R_18()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_69() {
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_3_4()) {
+            jj_scanpos = xsp;
+            if (jj_3R_76()) {
+                jj_scanpos = xsp;
+                if (jj_3_5()) {
+                    jj_scanpos = xsp;
+                    if (jj_3R_77()) {
+                        jj_scanpos = xsp;
+                        if (jj_3R_78()) {
+                            jj_scanpos = xsp;
+                            if (jj_3R_79()) {
+                                return true;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_26() {
+        if (jj_scan_token(LPAREN)) {
+            return true;
+        }
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_3R_30()) {
+            jj_scanpos = xsp;
+        }
+        if (jj_scan_token(RPAREN)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_20() {
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_3R_25()) {
+            jj_scanpos = xsp;
+            if (jj_3R_26()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_25() {
+        if (jj_3R_29()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_45() {
+        if (jj_scan_token(ASSIGN)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3_2() {
+        if (jj_3R_17()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3_3() {
+        if (jj_3R_17()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_17() {
+        if (jj_3R_20()) {
+            return true;
+        }
+        if (jj_scan_token(ARROW)) {
+            return true;
+        }
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_3_2()) {
+            jj_scanpos = xsp;
+            if (jj_3R_21()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_32() {
+        if (jj_3R_35()) {
+            return true;
+        }
+        Token xsp;
+        while (true) {
+            xsp = jj_scanpos;
+            if (jj_3R_36()) {
+                jj_scanpos = xsp;
+                break;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_41() {
+        if (jj_scan_token(SEMICOLON)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_18() {
+        if (jj_scan_token(LPAREN)) {
+            return true;
+        }
+        if (jj_3R_20()) {
+            return true;
+        }
+        if (jj_scan_token(ARROW)) {
+            return true;
+        }
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_3_3()) {
+            jj_scanpos = xsp;
+            if (jj_3R_22()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_43() {
+        if (jj_3R_27()) {
+            return true;
+        }
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_3R_45()) {
+            jj_scanpos = xsp;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_40() {
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_3_1()) {
+            jj_scanpos = xsp;
+            if (jj_3R_43()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3_1() {
+        if (jj_3R_17()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_28() {
+        if (jj_scan_token(LPAREN)) {
+            return true;
+        }
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_3R_32()) {
+            jj_scanpos = xsp;
+        }
+        if (jj_scan_token(RPAREN)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_38() {
+        if (jj_3R_40()) {
+            return true;
+        }
+        Token xsp;
+        while (true) {
+            xsp = jj_scanpos;
+            if (jj_3R_41()) {
+                jj_scanpos = xsp;
+                break;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_100() {
+        if (jj_scan_token(LBRACK)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_35() {
+        if (jj_3R_38()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_98() {
+        if (jj_3R_100()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_99() {
+        if (jj_scan_token(DOT)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_97() {
+        if (jj_3R_99()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_96() {
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_3R_97()) {
+            jj_scanpos = xsp;
+            if (jj_3R_98()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_95() {
+        if (jj_3R_96()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_62() {
+        if (jj_3R_69()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_57() {
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_3R_61()) {
+            jj_scanpos = xsp;
+            if (jj_3R_62()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_61() {
+        if (jj_3R_68()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_55() {
+        if (jj_3R_57()) {
+            return true;
+        }
+        Token xsp;
+        while (true) {
+            xsp = jj_scanpos;
+            if (jj_3R_95()) {
+                jj_scanpos = xsp;
+                break;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_53() {
+        if (jj_3R_55()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_52() {
+        if (jj_scan_token(EMPTY)) {
+            return true;
+        }
+        if (jj_3R_48()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_51() {
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_scan_token(39)) {
+            jj_scanpos = xsp;
+            if (jj_scan_token(40)) {
+                return true;
+            }
+        }
+        if (jj_3R_48()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_87() {
+        if (jj_scan_token(NULL)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_48() {
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_3R_50()) {
+            jj_scanpos = xsp;
+            if (jj_3R_51()) {
+                jj_scanpos = xsp;
+                if (jj_3R_52()) {
+                    jj_scanpos = xsp;
+                    if (jj_3R_53()) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_50() {
+        if (jj_scan_token(MINUS)) {
+            return true;
+        }
+        if (jj_3R_48()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_86() {
+        if (jj_scan_token(STRING_LITERAL)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_92() {
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_scan_token(53)) {
+            jj_scanpos = xsp;
+            if (jj_scan_token(54)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_91() {
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_scan_token(51)) {
+            jj_scanpos = xsp;
+            if (jj_scan_token(52)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_90() {
+        if (jj_scan_token(MULT)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_80() {
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_3R_90()) {
+            jj_scanpos = xsp;
+            if (jj_3R_91()) {
+                jj_scanpos = xsp;
+                if (jj_3R_92()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_85() {
+        if (jj_scan_token(INTEGER_LITERAL)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_46() {
+        if (jj_3R_48()) {
+            return true;
+        }
+        Token xsp;
+        while (true) {
+            xsp = jj_scanpos;
+            if (jj_3R_80()) {
+                jj_scanpos = xsp;
+                break;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_84() {
+        if (jj_scan_token(FLOATING_POINT_LITERAL)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_82() {
+        if (jj_scan_token(MINUS)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_70() {
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_3R_81()) {
+            jj_scanpos = xsp;
+            if (jj_3R_82()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_81() {
+        if (jj_scan_token(PLUS)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_94() {
+        if (jj_scan_token(FALSE)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_93() {
+        if (jj_scan_token(TRUE)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_83() {
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_3R_93()) {
+            jj_scanpos = xsp;
+            if (jj_3R_94()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_44() {
+        if (jj_3R_46()) {
+            return true;
+        }
+        Token xsp;
+        while (true) {
+            xsp = jj_scanpos;
+            if (jj_3R_70()) {
+                jj_scanpos = xsp;
+                break;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_63() {
+        if (jj_scan_token(CONCAT)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_75() {
+        if (jj_3R_87()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_74() {
+        if (jj_3R_86()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_73() {
+        if (jj_3R_85()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_72() {
+        if (jj_3R_84()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_42() {
+        if (jj_3R_44()) {
+            return true;
+        }
+        Token xsp;
+        while (true) {
+            xsp = jj_scanpos;
+            if (jj_3R_63()) {
+                jj_scanpos = xsp;
+                break;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_67() {
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_scan_token(31)) {
+            jj_scanpos = xsp;
+            if (jj_scan_token(32)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_68() {
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_3R_71()) {
+            jj_scanpos = xsp;
+            if (jj_3R_72()) {
+                jj_scanpos = xsp;
+                if (jj_3R_73()) {
+                    jj_scanpos = xsp;
+                    if (jj_3R_74()) {
+                        jj_scanpos = xsp;
+                        if (jj_3R_75()) {
+                            return true;
+                        }
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_71() {
+        if (jj_3R_83()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_66() {
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_scan_token(33)) {
+            jj_scanpos = xsp;
+            if (jj_scan_token(34)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_23() {
+        if (jj_scan_token(COLON)) {
+            return true;
+        }
+        if (jj_scan_token(IDENTIFIER)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_65() {
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_scan_token(27)) {
+            jj_scanpos = xsp;
+            if (jj_scan_token(28)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_58() {
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_3R_64()) {
+            jj_scanpos = xsp;
+            if (jj_3R_65()) {
+                jj_scanpos = xsp;
+                if (jj_3R_66()) {
+                    jj_scanpos = xsp;
+                    if (jj_3R_67()) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_64() {
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_scan_token(29)) {
+            jj_scanpos = xsp;
+            if (jj_scan_token(30)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_24() {
+        if (jj_3R_28()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_39() {
+        if (jj_3R_42()) {
+            return true;
+        }
+        Token xsp;
+        while (true) {
+            xsp = jj_scanpos;
+            if (jj_3R_58()) {
+                jj_scanpos = xsp;
+                break;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_60() {
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_scan_token(37)) {
+            jj_scanpos = xsp;
+            if (jj_scan_token(38)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_19() {
+        if (jj_scan_token(IDENTIFIER)) {
+            return true;
+        }
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_3R_23()) {
+            jj_scanpos = xsp;
+        }
+        if (jj_3R_24()) {
+            return true;
+        }
+        while (true) {
+            xsp = jj_scanpos;
+            if (jj_3R_24()) {
+                jj_scanpos = xsp;
+                break;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_56() {
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_3R_59()) {
+            jj_scanpos = xsp;
+            if (jj_3R_60()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_59() {
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_scan_token(35)) {
+            jj_scanpos = xsp;
+            if (jj_scan_token(36)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_54() {
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_scan_token(41)) {
+            jj_scanpos = xsp;
+            if (jj_scan_token(42)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_37() {
+        if (jj_3R_39()) {
+            return true;
+        }
+        Token xsp;
+        while (true) {
+            xsp = jj_scanpos;
+            if (jj_3R_56()) {
+                jj_scanpos = xsp;
+                break;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_29() {
+        if (jj_scan_token(IDENTIFIER)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_34() {
+        if (jj_3R_37()) {
+            return true;
+        }
+        Token xsp;
+        while (true) {
+            xsp = jj_scanpos;
+            if (jj_3R_54()) {
+                jj_scanpos = xsp;
+                break;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_102() {
+        if (jj_3R_35()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_21() {
+        if (jj_3R_27()) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean jj_3R_49() {
+        Token xsp;
+        xsp = jj_scanpos;
+        if (jj_scan_token(43)) {
+            jj_scanpos = xsp;
+            if (jj_scan_token(44)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean jj_3R_22() {
+        if (jj_3R_27()) {
+            return true;
+        }
+        return false;
+    }
+
+    /** Generated Token Manager. */
+    public ELParserTokenManager token_source;
+    SimpleCharStream jj_input_stream;
+    /** Current token. */
+    public Token token;
+    /** Next token. */
+    public Token jj_nt;
+    private int jj_ntk;
+    private Token jj_scanpos, jj_lastpos;
+    private int jj_la;
+    private int jj_gen;
+    final private int[] jj_la1 = new int[53];
+    static private int[] jj_la1_0;
+    static private int[] jj_la1_1;
+    static {
+        jj_la1_init_0();
+        jj_la1_init_1();
+    }
+
+    private static void jj_la1_init_0() {
+        jj_la1_0 = new int[] { 0xe, 0xe, 0x4000000, 0x0, 0x575a00, 0x575a00, 0x2000000, 0x0, 0x100000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf8000000,
+                0x60000000, 0x18000000, 0x0, 0x80000000, 0xf8000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x575a00, 0x480000, 0x575a00, 0x480000, 0x100000,
+                0x100000, 0x2000000, 0x575a00, 0x575a00, 0x100000, 0x100000, 0x400200, 0x2000000, 0x575a00, 0x1000000, 0x2000000, 0x575a00, 0x1000000, 0x100000,
+                0x75800, 0x30000, };
+    }
+
+    private static void jj_la1_init_1() {
+        jj_la1_1 = new int[] { 0x0, 0x0, 0x0, 0x1000000, 0x4022180, 0x4022180, 0x0, 0x4000000, 0x4000000, 0x40000, 0x1800, 0x1800, 0x600, 0x600, 0x78, 0x18,
+                0x60, 0x78, 0x7, 0x0, 0x0, 0x6, 0x1, 0x7, 0x800000, 0x30000, 0x30000, 0x788000, 0x180000, 0x600000, 0x788000, 0x180, 0x4022180, 0x0, 0x4000000,
+                0x0, 0x0, 0x0, 0x0, 0x4022180, 0x4022180, 0x0, 0x0, 0x4000000, 0x0, 0x4022180, 0x0, 0x0, 0x4022180, 0x0, 0x0, 0x0, 0x0, };
+    }
+
+    final private JJCalls[] jj_2_rtns = new JJCalls[5];
+    private boolean jj_rescan = false;
+    private int jj_gc = 0;
+
+    /** Constructor with InputStream. */
+    public ELParser(java.io.InputStream stream) {
+        this(stream, null);
+    }
+
+    /** Constructor with InputStream and supplied encoding */
+    public ELParser(java.io.InputStream stream, String encoding) {
+        try {
+            jj_input_stream = new SimpleCharStream(stream, encoding, 1, 1);
+        } catch (java.io.UnsupportedEncodingException e) {
+            throw new RuntimeException(e);
+        }
+        token_source = new ELParserTokenManager(jj_input_stream);
+        token = new Token();
+        jj_ntk = -1;
+        jj_gen = 0;
+        for (int i = 0; i < 53; i++) {
+            jj_la1[i] = -1;
+        }
+        for (int i = 0; i < jj_2_rtns.length; i++) {
+            jj_2_rtns[i] = new JJCalls();
+        }
+    }
+
+    /** Reinitialise. */
+    public void ReInit(java.io.InputStream stream) {
+        ReInit(stream, null);
+    }
+
+    /** Reinitialise. */
+    public void ReInit(java.io.InputStream stream, String encoding) {
+        try {
+            jj_input_stream.ReInit(stream, encoding, 1, 1);
+        } catch (java.io.UnsupportedEncodingException e) {
+            throw new RuntimeException(e);
+        }
+        token_source.ReInit(jj_input_stream);
+        token = new Token();
+        jj_ntk = -1;
+        jjtree.reset();
+        jj_gen = 0;
+        for (int i = 0; i < 53; i++) {
+            jj_la1[i] = -1;
+        }
+        for (int i = 0; i < jj_2_rtns.length; i++) {
+            jj_2_rtns[i] = new JJCalls();
+        }
+    }
+
+    /** Constructor. */
+    public ELParser(java.io.Reader stream) {
+        jj_input_stream = new SimpleCharStream(stream, 1, 1);
+        token_source = new ELParserTokenManager(jj_input_stream);
+        token = new Token();
+        jj_ntk = -1;
+        jj_gen = 0;
+        for (int i = 0; i < 53; i++) {
+            jj_la1[i] = -1;
+        }
+        for (int i = 0; i < jj_2_rtns.length; i++) {
+            jj_2_rtns[i] = new JJCalls();
+        }
+    }
+
+    /** Reinitialise. */
+    public void ReInit(java.io.Reader stream) {
+        jj_input_stream.ReInit(stream, 1, 1);
+        token_source.ReInit(jj_input_stream);
+        token = new Token();
+        jj_ntk = -1;
+        jjtree.reset();
+        jj_gen = 0;
+        for (int i = 0; i < 53; i++) {
+            jj_la1[i] = -1;
+        }
+        for (int i = 0; i < jj_2_rtns.length; i++) {
+            jj_2_rtns[i] = new JJCalls();
+        }
+    }
+
+    /** Constructor with generated Token Manager. */
+    public ELParser(ELParserTokenManager tm) {
+        token_source = tm;
+        token = new Token();
+        jj_ntk = -1;
+        jj_gen = 0;
+        for (int i = 0; i < 53; i++) {
+            jj_la1[i] = -1;
+        }
+        for (int i = 0; i < jj_2_rtns.length; i++) {
+            jj_2_rtns[i] = new JJCalls();
+        }
+    }
+
+    /** Reinitialise. */
+    public void ReInit(ELParserTokenManager tm) {
+        token_source = tm;
+        token = new Token();
+        jj_ntk = -1;
+        jjtree.reset();
+        jj_gen = 0;
+        for (int i = 0; i < 53; i++) {
+            jj_la1[i] = -1;
+        }
+        for (int i = 0; i < jj_2_rtns.length; i++) {
+            jj_2_rtns[i] = new JJCalls();
+        }
+    }
+
+    private Token jj_consume_token(int kind) throws ParseException {
+        Token oldToken;
+        if ((oldToken = token).next != null) {
+            token = token.next;
+        } else {
+            token = token.next = token_source.getNextToken();
+        }
+        jj_ntk = -1;
+        if (token.kind == kind) {
+            jj_gen++;
+            if (++jj_gc > 100) {
+                jj_gc = 0;
+                for (int i = 0; i < jj_2_rtns.length; i++) {
+                    JJCalls c = jj_2_rtns[i];
+                    while (c != null) {
+                        if (c.gen < jj_gen) {
+                            c.first = null;
+                        }
+                        c = c.next;
+                    }
+                }
+            }
+            return token;
+        }
+        token = oldToken;
+        jj_kind = kind;
+        throw generateParseException();
+    }
+
+    static private final class LookaheadSuccess extends java.lang.Error {
+    }
+
+    static final private LookaheadSuccess jj_ls = new LookaheadSuccess();
+
+    private boolean jj_scan_token(int kind) {
+        if (jj_scanpos == jj_lastpos) {
+            jj_la--;
+            if (jj_scanpos.next == null) {
+                jj_lastpos = jj_scanpos = jj_scanpos.next = token_source.getNextToken();
+            } else {
+                jj_lastpos = jj_scanpos = jj_scanpos.next;
+            }
+        } else {
+            jj_scanpos = jj_scanpos.next;
+        }
+        if (jj_rescan) {
+            int i = 0;
+            Token tok = token;
+            while (tok != null && tok != jj_scanpos) {
+                i++;
+                tok = tok.next;
+            }
+            if (tok != null) {
+                jj_add_error_token(kind, i);
+            }
+        }
+        if (jj_scanpos.kind != kind) {
+            return true;
+        }
+        if (jj_la == 0 && jj_scanpos == jj_lastpos) {
+            throw jj_ls;
+        }
+        return false;
+    }
+
+    /** Get the next Token. */
+    final public Token getNextToken() {
+        if (token.next != null) {
+            token = token.next;
+        } else {
+            token = token.next = token_source.getNextToken();
+        }
+        jj_ntk = -1;
+        jj_gen++;
+        return token;
+    }
+
+    /** Get the specific Token. */
+    final public Token getToken(int index) {
+        Token t = token;
+        for (int i = 0; i < index; i++) {
+            if (t.next != null) {
+                t = t.next;
+            } else {
+                t = t.next = token_source.getNextToken();
+            }
+        }
+        return t;
+    }
+
+    private int jj_ntk() {
+        if ((jj_nt = token.next) == null) {
+            return (jj_ntk = (token.next = token_source.getNextToken()).kind);
+        } else {
+            return (jj_ntk = jj_nt.kind);
+        }
+    }
+
+    private java.util.List<int[]> jj_expentries = new java.util.ArrayList<int[]>();
+    private int[] jj_expentry;
+    private int jj_kind = -1;
+    private int[] jj_lasttokens = new int[100];
+    private int jj_endpos;
+
+    private void jj_add_error_token(int kind, int pos) {
+        if (pos >= 100) {
+            return;
+        }
+        if (pos == jj_endpos + 1) {
+            jj_lasttokens[jj_endpos++] = kind;
+        } else if (jj_endpos != 0) {
+            jj_expentry = new int[jj_endpos];
+            for (int i = 0; i < jj_endpos; i++) {
+                jj_expentry[i] = jj_lasttokens[i];
+            }
+            jj_entries_loop: for (java.util.Iterator<?> it = jj_expentries.iterator(); it.hasNext();) {
+                int[] oldentry = (int[]) (it.next());
+                if (oldentry.length == jj_expentry.length) {
+                    for (int i = 0; i < jj_expentry.length; i++) {
+                        if (oldentry[i] != jj_expentry[i]) {
+                            continue jj_entries_loop;
+                        }
+                    }
+                    jj_expentries.add(jj_expentry);
+                    break jj_entries_loop;
+                }
+            }
+            if (pos != 0) {
+                jj_lasttokens[(jj_endpos = pos) - 1] = kind;
+            }
+        }
+    }
+
+    /** Generate ParseException. */
+    public ParseException generateParseException() {
+        jj_expentries.clear();
+        boolean[] la1tokens = new boolean[63];
+        if (jj_kind >= 0) {
+            la1tokens[jj_kind] = true;
+            jj_kind = -1;
+        }
+        for (int i = 0; i < 53; i++) {
+            if (jj_la1[i] == jj_gen) {
+                for (int j = 0; j < 32; j++) {
+                    if ((jj_la1_0[i] & (1 << j)) != 0) {
+                        la1tokens[j] = true;
+                    }
+                    if ((jj_la1_1[i] & (1 << j)) != 0) {
+                        la1tokens[32 + j] = true;
+                    }
+                }
+            }
+        }
+        for (int i = 0; i < 63; i++) {
+            if (la1tokens[i]) {
+                jj_expentry = new int[1];
+                jj_expentry[0] = i;
+                jj_expentries.add(jj_expentry);
+            }
+        }
+        jj_endpos = 0;
+        jj_rescan_token();
+        jj_add_error_token(0, 0);
+        int[][] exptokseq = new int[jj_expentries.size()][];
+        for (int i = 0; i < jj_expentries.size(); i++) {
+            exptokseq[i] = jj_expentries.get(i);
+        }
+        return new ParseException(token, exptokseq, tokenImage);
+    }
+
+    /** Enable tracing. */
+    final public void enable_tracing() {
+    }
+
+    /** Disable tracing. */
+    final public void disable_tracing() {
+    }
+
+    private void jj_rescan_token() {
+        jj_rescan = true;
+        for (int i = 0; i < 5; i++) {
+            try {
+                JJCalls p = jj_2_rtns[i];
+                do {
+                    if (p.gen > jj_gen) {
+                        jj_la = p.arg;
+                        jj_lastpos = jj_scanpos = p.first;
+                        switch (i) {
+                        case 0:
+                            jj_3_1();
+                            break;
+                        case 1:
+                            jj_3_2();
+                            break;
+                        case 2:
+                            jj_3_3();
+                            break;
+                        case 3:
+                            jj_3_4();
+                            break;
+                        case 4:
+                            jj_3_5();
+                            break;
+                        }
+                    }
+                    p = p.next;
+                } while (p != null);
+            } catch (LookaheadSuccess ls) {
+            }
+        }
+        jj_rescan = false;
+    }
+
+    private void jj_save(int index, int xla) {
+        JJCalls p = jj_2_rtns[index];
+        while (p.gen > jj_gen) {
+            if (p.next == null) {
+                p = p.next = new JJCalls();
+                break;
+            }
+            p = p.next;
+        }
+        p.gen = jj_gen + xla - jj_la;
+        p.first = token;
+        p.arg = xla;
+    }
+
+    static final class JJCalls {
+        int gen;
+        Token first;
+        int arg;
+        JJCalls next;
+    }
+
+}
diff --git a/impl/src/main/java/com/sun/el/parser/ELParser.jjt b/impl/src/main/java/com/sun/el/parser/ELParser.jjt
new file mode 100644
index 0000000..4cccc57
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/ELParser.jjt
@@ -0,0 +1,573 @@
+/*
+ * Copyright (c) 1997, 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/*
+	Author:	Jacob Hookom
+	Email:	jacob at hookom.net
+
+        Author: Kin-man Chung (EL 2.2 and EL 3.0)
+*/
+
+/* == Option Declaration == */
+options
+{
+	STATIC=false;
+	NODE_PREFIX="Ast";
+	VISITOR_EXCEPTION="javax.el.ELException";
+	VISITOR=false;
+	MULTI=true;
+	NODE_DEFAULT_VOID=true;
+	JAVA_UNICODE_ESCAPE=false;
+  	UNICODE_INPUT=true;
+	BUILD_NODE_FILES=true;
+}
+
+/* == Parser Declaration == */
+PARSER_BEGIN( ELParser )
+package com.sun.el.parser;
+import java.io.StringReader;
+import javax.el.ELException;
+public class ELParser
+{
+    public static Node parse(String ref) throws ELException
+    {
+        try {
+        	return (new ELParser(new StringReader(ref))).CompositeExpression();
+        } catch (ParseException pe) {
+           	throw new ELException(pe.getMessage());
+        }
+    }
+}
+PARSER_END( ELParser )
+
+/*
+ * CompositeExpression
+ * Allow most flexible parsing, restrict by examining
+ * type of returned node
+ */
+AstCompositeExpression CompositeExpression() #CompositeExpression : {}
+{
+	(DeferredExpression() | DynamicExpression() | LiteralExpression())* <EOF> { return jjtThis; }
+}
+
+/*
+ * LiteralExpression
+ * Non-EL Expression blocks
+ */
+void LiteralExpression() #LiteralExpression : { Token t = null; }
+{
+	t=<LITERAL_EXPRESSION> { jjtThis.setImage(t.image); }
+}
+
+/*
+ * DeferredExpression
+ * #{..} Expressions
+ */
+void DeferredExpression() #DeferredExpression : {}
+{
+	<START_DEFERRED_EXPRESSION> Expression() <RCURL> 
+}
+
+/*
+ * DynamicExpression
+ * ${..} Expressions
+ */
+void DynamicExpression() #DynamicExpression : {}
+{
+	<START_DYNAMIC_EXPRESSION> Expression() <RCURL> 
+}
+
+/*
+ * Expression
+ * EL Expression Language Root
+ */
+void Expression() : {}
+{
+        SemiColon()
+}
+
+/*
+ * SemiColon
+ */
+void SemiColon() : {}
+{
+        Assignment() (<SEMICOLON> Assignment() #SemiColon(2) )*
+}
+
+/*
+ * Assignment
+ * For '=', right associatve, then LambdaExpression or Choice or Assignment
+ */
+void Assignment() : {}
+{
+        LOOKAHEAD(3) LambdaExpression() |
+        Choice() (<ASSIGN> Assignment() #Assign(2) )?
+}
+
+/*
+ * LambdaExpression
+ */
+void LambdaExpression() #LambdaExpression : {}
+{
+       LambdaParameters() <ARROW>
+       (LOOKAHEAD(3) LambdaExpression() | Choice() )
+}
+
+void LambdaParameters() #LambdaParameters: {}
+{
+       Identifier()
+       | <LPAREN>
+            (Identifier() (<COMMA> Identifier())*)?
+         <RPAREN>
+}
+
+/*
+ * Choice
+ * For Choice markup a ? b : c, right associative
+ */
+void Choice() : {}
+{
+	Or() (<QUESTIONMARK> Choice() <COLON> Choice() #Choice(3))?
+}
+
+/*
+ * Or
+ * For 'or' '||', then And
+ */
+void Or() : {}
+{
+	And() ((<OR0>|<OR1>) And() #Or(2))*
+}
+
+/*
+ * And
+ * For 'and' '&&', then Equality
+ */
+void And() : {}
+{
+	Equality() ((<AND0>|<AND1>) Equality() #And(2))*
+}
+
+/*
+ * Equality
+ * For '==' 'eq' '!=' 'ne', then Compare
+ */
+void Equality() : {}
+{
+	Compare()
+	(
+		((<EQ0>|<EQ1>) Compare() #Equal(2))
+	|
+		((<NE0>|<NE1>) Compare() #NotEqual(2))
+	)*
+}
+
+/*
+ * Compare
+ * For a bunch of them, then Math
+ */
+void Compare() : {}
+{
+	Concatenation()
+	(
+		((<LT0>|<LT1>) Concatenation() #LessThan(2))
+	|
+		((<GT0>|<GT1>) Concatenation() #GreaterThan(2))
+	|
+		((<LE0>|<LE1>) Concatenation() #LessThanEqual(2))
+	|
+		((<GE0>|<GE1>) Concatenation() #GreaterThanEqual(2))
+	)*
+}
+
+/*
+ * Concatenation
+ * For '&', then Math()
+ */
+void Concatenation() : {}
+{
+        Math() ( <CONCAT> Math() #Concat(2) )*
+}
+
+/*
+ * Math
+ * For '+' '-', then Multiplication
+ */
+void Math() : {}
+{
+	Multiplication()
+	(
+		(<PLUS> Multiplication() #Plus(2))
+	|
+		(<MINUS> Multiplication() #Minus(2))
+	)*
+}
+
+/*
+ * Multiplication
+ * For a bunch of them, then Unary
+ */
+void Multiplication() : {}
+{
+	Unary()
+	(
+		(<MULT> Unary() #Mult(2))
+	|
+		((<DIV0>|<DIV1>) Unary() #Div(2))
+	|
+		((<MOD0>|<MOD1>) Unary() #Mod(2))	
+	)*	
+}
+
+/*
+ * Unary
+ * For '-' '!' 'not' 'empty', then Value
+ */
+void Unary() : {}
+{
+		<MINUS> Unary() #Negative 
+	|
+		(<NOT0>|<NOT1>) Unary() #Not 
+	|
+		<EMPTY> Unary() #Empty
+	|	
+		Value()
+}
+
+/*
+ * Value
+ * Defines Prefix plus zero or more Suffixes
+ */
+void Value() : {}
+{
+	(ValuePrefix() (ValueSuffix())*) #Value(>1)
+}
+
+/*
+ * ValuePrefix
+ * For Literals, Variables, and Functions
+ */
+void ValuePrefix() : {}
+{
+	Literal()
+	| NonLiteral()
+}
+
+/*
+ * ValueSuffix
+ * Either dot or bracket notation
+ */
+void ValueSuffix() : {}
+{
+	DotSuffix() | BracketSuffix()
+}
+
+/*
+ * DotSuffix
+ * Dot Property and Dot Method
+ */
+void DotSuffix() #DotSuffix : {  Token t = null; }
+{
+        <DOT> t=<IDENTIFIER> { jjtThis.setImage(t.image); }
+        (MethodArguments())?
+}
+
+/*
+ * BracketSuffix
+ * Sub Expression Suffix
+ */
+void BracketSuffix() #BracketSuffix : {}
+{
+	<LBRACK> Expression() <RBRACK>
+        (MethodArguments())?
+}
+
+
+/*
+ * MethodArguments
+ */
+void MethodArguments() #MethodArguments : {}
+{
+        <LPAREN> (Expression() (<COMMA> Expression())*)? <RPAREN>
+}
+
+/*
+ * Parenthesized Lambda Expression, with optional invokation
+ */
+void LambdaExpressionOrCall() #LambdaExpression : {}
+
+{
+    <LPAREN>
+        LambdaParameters() <ARROW>
+        (LOOKAHEAD(3) LambdaExpression() | Choice() )
+    <RPAREN>
+    (MethodArguments())*
+}
+
+/*
+ * NonLiteral
+ * For Grouped Operations, Identifiers, and Functions
+ */
+void NonLiteral() : {}
+{
+        LOOKAHEAD(4) LambdaExpressionOrCall()
+ 	| <LPAREN> Expression() <RPAREN>
+ 	| LOOKAHEAD(4) Function()
+	| Identifier()
+        | MapData()
+        | ListData()
+}
+
+void MapData() #MapData: {}
+{
+    <START_MAP>
+        ( MapEntry() ( <COMMA> MapEntry() )* )?
+    <RCURL>
+}
+
+void MapEntry() #MapEntry: {}
+{
+    Expression() (<COLON> Expression())?
+}
+
+void ListData() #ListData: {}
+{
+    <LBRACK>
+        ( Expression() ( <COMMA> Expression() )* )?
+    <RBRACK>
+}
+
+/*
+ * Identifier
+ * Java Language Identifier
+ */
+void Identifier() #Identifier : { Token t = null; }
+{
+	t=<IDENTIFIER> { jjtThis.setImage(t.image); }
+}
+
+/*
+ * Function
+ * Namespace:Name(a,b,c)
+ */
+void Function() #Function :
+{
+	Token t0 = null;
+	Token t1 = null;
+}
+{
+	t0=<IDENTIFIER> (<COLON> t1=<IDENTIFIER>)?
+	{
+		if (t1 != null) {
+			jjtThis.setPrefix(t0.image);
+			jjtThis.setLocalName(t1.image);
+		} else {
+			jjtThis.setLocalName(t0.image);
+		}
+	}
+        (MethodArguments())+
+}
+
+
+/*
+ * Literal
+ * Reserved Keywords
+ */
+void Literal() : {}
+{
+	Boolean()
+	| FloatingPoint()
+	| Integer()
+	| String()
+	| Null()
+}
+
+/*
+ * Boolean
+ * For 'true' 'false'
+ */
+void Boolean() : {}
+{
+	<TRUE> #True
+	| <FALSE> #False
+}
+
+/*
+ * FloatinPoint
+ * For Decimal and Floating Point Literals
+ */
+void FloatingPoint() #FloatingPoint : { Token t = null; }
+{
+	t=<FLOATING_POINT_LITERAL> { jjtThis.setImage(t.image); }
+}
+
+/*
+ * Integer
+ * For Simple Numeric Literals
+ */
+void Integer() #Integer : { Token t = null; }
+{
+	t=<INTEGER_LITERAL> { jjtThis.setImage(t.image); }
+}
+
+/*
+ * String
+ * For Quoted Literals
+ */
+void String() #String : { Token t = null; }
+{
+	t=<STRING_LITERAL> { jjtThis.setImage(t.image); }
+}
+
+/*
+ * Null
+ * For 'null'
+ */
+void Null() #Null : {}
+{
+	<NULL>
+}
+
+
+/* ========================================================================== */
+TOKEN_MGR_DECLS:
+{
+    java.util.Stack<Integer> stack = new java.util.Stack<Integer>();
+}
+
+<DEFAULT> TOKEN :
+{
+  < LITERAL_EXPRESSION:
+    ((~["\\", "$", "#"])
+      | ("\\" ("\\" | "$" | "#"))
+      | ("$" ~["{", "$", "#", "\\"])
+      | ("#" ~["{", "$", "#", "\\"])
+    )+
+    | "$"
+    | "#"
+  >
+|
+  < START_DYNAMIC_EXPRESSION: "${" > {stack.push(DEFAULT);}: IN_EXPRESSION
+|
+  < START_DEFERRED_EXPRESSION: "#{" > {stack.push(DEFAULT);}: IN_EXPRESSION
+}
+
+<DEFAULT> SKIP : { "\\" }
+
+<IN_EXPRESSION, IN_MAP> SKIP:
+{ " " | "\t" | "\n" | "\r" }
+
+<IN_EXPRESSION, IN_MAP> TOKEN :
+{
+        < START_MAP : "{" > {stack.push(curLexState);}: IN_MAP
+|       < RCURL: "}" > {SwitchTo(stack.pop());}
+|       < INTEGER_LITERAL: ["0"-"9"] (["0"-"9"])* >
+|	< FLOATING_POINT_LITERAL: (["0"-"9"])+ "." (["0"-"9"])* (<EXPONENT>)? 
+		| "." (["0"-"9"])+ (<EXPONENT>)?
+		| (["0"-"9"])+ <EXPONENT>
+	>
+|	< #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ >
+|	< STRING_LITERAL: ("\"" ((~["\"","\\"])
+		| ("\\" ( ["\\","\""] )))* "\"")
+		| ("\'" ((~["\'","\\"])
+		| ("\\" ( ["\\","\'"] )))* "\'")
+	>
+|	< BADLY_ESCAPED_STRING_LITERAL: ("\"" (~["\"","\\"])* ("\\" ( ~["\\","\""] )))
+		| ("\'" (~["\'","\\"])* ("\\" ( ~["\\","\'"] )))
+	>
+|	< TRUE : "true" >
+|	< FALSE : "false" >
+|	< NULL : "null" >
+|	< DOT : "." >
+|	< LPAREN : "(" >
+|	< RPAREN : ")" >
+|	< LBRACK : "[" >
+|	< RBRACK : "]" >
+|	< COLON : ":" >
+|	< COMMA : "," >
+|       < SEMICOLON : ";" >
+|	< GT0 : ">" >
+|	< GT1 : "gt" >
+|	< LT0 : "<" >
+|	< LT1 : "lt" >
+|	< GE0 : ">=" >
+|	< GE1 : "ge" >
+|	< LE0 : "<=" >
+|	< LE1 : "le" >
+|	< EQ0 : "==" >
+|	< EQ1 : "eq" >
+|	< NE0 : "!=" >
+|	< NE1 : "ne" >
+|	< NOT0 : "!" >
+|	< NOT1 : "not" >
+|	< AND0 : "&&" >
+|	< AND1 : "and" >
+|	< OR0 : "||" >
+|	< OR1 : "or" >
+|	< EMPTY : "empty" >
+|	< INSTANCEOF : "instanceof" >
+|	< MULT : "*" >
+|	< PLUS : "+" >
+|	< MINUS : "-" >
+|	< QUESTIONMARK : "?" >
+|	< DIV0 : "/" >
+|	< DIV1 : "div" >
+|	< MOD0 : "%" >
+|	< MOD1 : "mod" >
+|       < CONCAT : "+=" >
+|       < ASSIGN : "=" >
+|       < ARROW : "->" >
+|	< IDENTIFIER : (<LETTER>|<IMPL_OBJ_START>) (<LETTER>|<DIGIT>)* >
+|	< #IMPL_OBJ_START: "#" >
+|	< #LETTER:
+		[
+		"\u0024",
+		"\u0041"-"\u005a",
+		"\u005f",
+		"\u0061"-"\u007a",
+		"\u00c0"-"\u00d6",
+		"\u00d8"-"\u00f6",
+		"\u00f8"-"\u00ff",
+		"\u0100"-"\u1fff",
+		"\u3040"-"\u318f",
+		"\u3300"-"\u337f",
+		"\u3400"-"\u3d2d",
+		"\u4e00"-"\u9fff",
+		"\uf900"-"\ufaff"
+		]
+	>
+|	< #DIGIT:
+		[
+		"\u0030"-"\u0039",
+		"\u0660"-"\u0669",
+		"\u06f0"-"\u06f9",
+		"\u0966"-"\u096f",
+		"\u09e6"-"\u09ef",
+		"\u0a66"-"\u0a6f",
+		"\u0ae6"-"\u0aef",
+		"\u0b66"-"\u0b6f",
+		"\u0be7"-"\u0bef",
+		"\u0c66"-"\u0c6f",
+		"\u0ce6"-"\u0cef",
+		"\u0d66"-"\u0d6f",
+		"\u0e50"-"\u0e59",
+		"\u0ed0"-"\u0ed9",
+		"\u1040"-"\u1049"
+		]
+	>
+|	< ILLEGAL_CHARACTER: (~[]) >
+}
+
diff --git a/impl/src/main/java/com/sun/el/parser/ELParserConstants.java b/impl/src/main/java/com/sun/el/parser/ELParserConstants.java
new file mode 100644
index 0000000..41445ce
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/ELParserConstants.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/* Generated By:JJTree&JavaCC: Do not edit this line. ELParserConstants.java */
+package com.sun.el.parser;
+
+/**
+ * Token literal values and constants. Generated by org.javacc.parser.OtherFilesGen#start()
+ */
+public interface ELParserConstants {
+
+    /** End of File. */
+    int EOF = 0;
+    /** RegularExpression Id. */
+    int LITERAL_EXPRESSION = 1;
+    /** RegularExpression Id. */
+    int START_DYNAMIC_EXPRESSION = 2;
+    /** RegularExpression Id. */
+    int START_DEFERRED_EXPRESSION = 3;
+    /** RegularExpression Id. */
+    int START_MAP = 9;
+    /** RegularExpression Id. */
+    int RCURL = 10;
+    /** RegularExpression Id. */
+    int INTEGER_LITERAL = 11;
+    /** RegularExpression Id. */
+    int FLOATING_POINT_LITERAL = 12;
+    /** RegularExpression Id. */
+    int EXPONENT = 13;
+    /** RegularExpression Id. */
+    int STRING_LITERAL = 14;
+    /** RegularExpression Id. */
+    int BADLY_ESCAPED_STRING_LITERAL = 15;
+    /** RegularExpression Id. */
+    int TRUE = 16;
+    /** RegularExpression Id. */
+    int FALSE = 17;
+    /** RegularExpression Id. */
+    int NULL = 18;
+    /** RegularExpression Id. */
+    int DOT = 19;
+    /** RegularExpression Id. */
+    int LPAREN = 20;
+    /** RegularExpression Id. */
+    int RPAREN = 21;
+    /** RegularExpression Id. */
+    int LBRACK = 22;
+    /** RegularExpression Id. */
+    int RBRACK = 23;
+    /** RegularExpression Id. */
+    int COLON = 24;
+    /** RegularExpression Id. */
+    int COMMA = 25;
+    /** RegularExpression Id. */
+    int SEMICOLON = 26;
+    /** RegularExpression Id. */
+    int GT0 = 27;
+    /** RegularExpression Id. */
+    int GT1 = 28;
+    /** RegularExpression Id. */
+    int LT0 = 29;
+    /** RegularExpression Id. */
+    int LT1 = 30;
+    /** RegularExpression Id. */
+    int GE0 = 31;
+    /** RegularExpression Id. */
+    int GE1 = 32;
+    /** RegularExpression Id. */
+    int LE0 = 33;
+    /** RegularExpression Id. */
+    int LE1 = 34;
+    /** RegularExpression Id. */
+    int EQ0 = 35;
+    /** RegularExpression Id. */
+    int EQ1 = 36;
+    /** RegularExpression Id. */
+    int NE0 = 37;
+    /** RegularExpression Id. */
+    int NE1 = 38;
+    /** RegularExpression Id. */
+    int NOT0 = 39;
+    /** RegularExpression Id. */
+    int NOT1 = 40;
+    /** RegularExpression Id. */
+    int AND0 = 41;
+    /** RegularExpression Id. */
+    int AND1 = 42;
+    /** RegularExpression Id. */
+    int OR0 = 43;
+    /** RegularExpression Id. */
+    int OR1 = 44;
+    /** RegularExpression Id. */
+    int EMPTY = 45;
+    /** RegularExpression Id. */
+    int INSTANCEOF = 46;
+    /** RegularExpression Id. */
+    int MULT = 47;
+    /** RegularExpression Id. */
+    int PLUS = 48;
+    /** RegularExpression Id. */
+    int MINUS = 49;
+    /** RegularExpression Id. */
+    int QUESTIONMARK = 50;
+    /** RegularExpression Id. */
+    int DIV0 = 51;
+    /** RegularExpression Id. */
+    int DIV1 = 52;
+    /** RegularExpression Id. */
+    int MOD0 = 53;
+    /** RegularExpression Id. */
+    int MOD1 = 54;
+    /** RegularExpression Id. */
+    int CONCAT = 55;
+    /** RegularExpression Id. */
+    int ASSIGN = 56;
+    /** RegularExpression Id. */
+    int ARROW = 57;
+    /** RegularExpression Id. */
+    int IDENTIFIER = 58;
+    /** RegularExpression Id. */
+    int IMPL_OBJ_START = 59;
+    /** RegularExpression Id. */
+    int LETTER = 60;
+    /** RegularExpression Id. */
+    int DIGIT = 61;
+    /** RegularExpression Id. */
+    int ILLEGAL_CHARACTER = 62;
+
+    /** Lexical state. */
+    int DEFAULT = 0;
+    /** Lexical state. */
+    int IN_EXPRESSION = 1;
+    /** Lexical state. */
+    int IN_MAP = 2;
+
+    /** Literal token values. */
+    String[] tokenImage = { "<EOF>", "<LITERAL_EXPRESSION>", "\"${\"", "\"#{\"", "\"\\\\\"", "\" \"", "\"\\t\"", "\"\\n\"", "\"\\r\"", "\"{\"", "\"}\"",
+            "<INTEGER_LITERAL>", "<FLOATING_POINT_LITERAL>", "<EXPONENT>", "<STRING_LITERAL>", "<BADLY_ESCAPED_STRING_LITERAL>", "\"true\"", "\"false\"",
+            "\"null\"", "\".\"", "\"(\"", "\")\"", "\"[\"", "\"]\"", "\":\"", "\",\"", "\";\"", "\">\"", "\"gt\"", "\"<\"", "\"lt\"", "\">=\"", "\"ge\"",
+            "\"<=\"", "\"le\"", "\"==\"", "\"eq\"", "\"!=\"", "\"ne\"", "\"!\"", "\"not\"", "\"&&\"", "\"and\"", "\"||\"", "\"or\"", "\"empty\"",
+            "\"instanceof\"", "\"*\"", "\"+\"", "\"-\"", "\"?\"", "\"/\"", "\"div\"", "\"%\"", "\"mod\"", "\"+=\"", "\"=\"", "\"->\"", "<IDENTIFIER>", "\"#\"",
+            "<LETTER>", "<DIGIT>", "<ILLEGAL_CHARACTER>", };
+
+}
diff --git a/impl/src/main/java/com/sun/el/parser/ELParserTokenManager.java b/impl/src/main/java/com/sun/el/parser/ELParserTokenManager.java
new file mode 100644
index 0000000..027fd5d
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/ELParserTokenManager.java
@@ -0,0 +1,2204 @@
+/*
+ * Copyright (c) 2018, 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/* Generated By:JJTree&JavaCC: Do not edit this line. ELParserTokenManager.java */
+package com.sun.el.parser;
+
+/** Token Manager. */
+public class ELParserTokenManager implements ELParserConstants {
+    java.util.Stack<Integer> stack = new java.util.Stack<Integer>();
+
+    /** Debug output. */
+    public java.io.PrintStream debugStream = System.out;
+
+    /** Set debug output. */
+    public void setDebugStream(java.io.PrintStream ds) {
+        debugStream = ds;
+    }
+
+    private final int jjStopStringLiteralDfa_0(int pos, long active0) {
+        switch (pos) {
+        case 0:
+            if ((active0 & 0x10L) != 0L) {
+                return 2;
+            }
+            if ((active0 & 0xcL) != 0L) {
+                jjmatchedKind = 1;
+                return 4;
+            }
+            return -1;
+        default:
+            return -1;
+        }
+    }
+
+    private final int jjStartNfa_0(int pos, long active0) {
+        return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0), pos + 1);
+    }
+
+    private int jjStopAtPos(int pos, int kind) {
+        jjmatchedKind = kind;
+        jjmatchedPos = pos;
+        return pos + 1;
+    }
+
+    private int jjMoveStringLiteralDfa0_0() {
+        switch (curChar) {
+        case 35:
+            return jjMoveStringLiteralDfa1_0(0x8L);
+        case 36:
+            return jjMoveStringLiteralDfa1_0(0x4L);
+        case 92:
+            return jjStartNfaWithStates_0(0, 4, 2);
+        default:
+            return jjMoveNfa_0(6, 0);
+        }
+    }
+
+    private int jjMoveStringLiteralDfa1_0(long active0) {
+        try {
+            curChar = input_stream.readChar();
+        } catch (java.io.IOException e) {
+            jjStopStringLiteralDfa_0(0, active0);
+            return 1;
+        }
+        switch (curChar) {
+        case 123:
+            if ((active0 & 0x4L) != 0L) {
+                return jjStopAtPos(1, 2);
+            } else if ((active0 & 0x8L) != 0L) {
+                return jjStopAtPos(1, 3);
+            }
+            break;
+        default:
+            break;
+        }
+        return jjStartNfa_0(0, active0);
+    }
+
+    private int jjStartNfaWithStates_0(int pos, int kind, int state) {
+        jjmatchedKind = kind;
+        jjmatchedPos = pos;
+        try {
+            curChar = input_stream.readChar();
+        } catch (java.io.IOException e) {
+            return pos + 1;
+        }
+        return jjMoveNfa_0(state, pos + 1);
+    }
+
+    static final long[] jjbitVec0 = { 0xfffffffffffffffeL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL };
+    static final long[] jjbitVec2 = { 0x0L, 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL };
+
+    private int jjMoveNfa_0(int startState, int curPos) {
+        int startsAt = 0;
+        jjnewStateCnt = 7;
+        int i = 1;
+        jjstateSet[0] = startState;
+        int kind = 0x7fffffff;
+        for (;;) {
+            if (++jjround == 0x7fffffff) {
+                ReInitRounds();
+            }
+            if (curChar < 64) {
+                long l = 1L << curChar;
+                do {
+                    switch (jjstateSet[--i]) {
+                    case 6:
+                        if ((0xffffffe7ffffffffL & l) != 0L) {
+                            if (kind > 1) {
+                                kind = 1;
+                            }
+                            jjCheckNAddStates(0, 3);
+                        } else if ((0x1800000000L & l) != 0L) {
+                            if (kind > 1) {
+                                kind = 1;
+                            }
+                        }
+                        if (curChar == 35) {
+                            jjCheckNAdd(4);
+                        } else if (curChar == 36) {
+                            jjCheckNAdd(4);
+                        }
+                        break;
+                    case 0:
+                    case 4:
+                        if ((0xffffffe7ffffffffL & l) == 0L) {
+                            break;
+                        }
+                        if (kind > 1) {
+                            kind = 1;
+                        }
+                        jjCheckNAddStates(0, 3);
+                        break;
+                    case 2:
+                        if ((0x1800000000L & l) == 0L) {
+                            break;
+                        }
+                        if (kind > 1) {
+                            kind = 1;
+                        }
+                        jjCheckNAddStates(0, 3);
+                        break;
+                    case 3:
+                        if (curChar == 36) {
+                            jjCheckNAdd(4);
+                        }
+                        break;
+                    case 5:
+                        if (curChar == 35) {
+                            jjCheckNAdd(4);
+                        }
+                        break;
+                    default:
+                        break;
+                    }
+                } while (i != startsAt);
+            } else if (curChar < 128) {
+                long l = 1L << (curChar & 077);
+                do {
+                    switch (jjstateSet[--i]) {
+                    case 6:
+                        if ((0xffffffffefffffffL & l) != 0L) {
+                            if (kind > 1) {
+                                kind = 1;
+                            }
+                            jjCheckNAddStates(0, 3);
+                        } else if (curChar == 92) {
+                            jjstateSet[jjnewStateCnt++] = 2;
+                        }
+                        break;
+                    case 0:
+                        if ((0xffffffffefffffffL & l) == 0L) {
+                            break;
+                        }
+                        if (kind > 1) {
+                            kind = 1;
+                        }
+                        jjCheckNAddStates(0, 3);
+                        break;
+                    case 1:
+                        if (curChar == 92) {
+                            jjstateSet[jjnewStateCnt++] = 2;
+                        }
+                        break;
+                    case 2:
+                        if (curChar != 92) {
+                            break;
+                        }
+                        if (kind > 1) {
+                            kind = 1;
+                        }
+                        jjCheckNAddStates(0, 3);
+                        break;
+                    case 4:
+                        if ((0xf7ffffffefffffffL & l) == 0L) {
+                            break;
+                        }
+                        if (kind > 1) {
+                            kind = 1;
+                        }
+                        jjCheckNAddStates(0, 3);
+                        break;
+                    default:
+                        break;
+                    }
+                } while (i != startsAt);
+            } else {
+                int hiByte = curChar >> 8;
+                int i1 = hiByte >> 6;
+                long l1 = 1L << (hiByte & 077);
+                int i2 = (curChar & 0xff) >> 6;
+                long l2 = 1L << (curChar & 077);
+                do {
+                    switch (jjstateSet[--i]) {
+                    case 6:
+                    case 0:
+                    case 4:
+                        if (!jjCanMove_0(hiByte, i1, i2, l1, l2)) {
+                            break;
+                        }
+                        if (kind > 1) {
+                            kind = 1;
+                        }
+                        jjCheckNAddStates(0, 3);
+                        break;
+                    default:
+                        break;
+                    }
+                } while (i != startsAt);
+            }
+            if (kind != 0x7fffffff) {
+                jjmatchedKind = kind;
+                jjmatchedPos = curPos;
+                kind = 0x7fffffff;
+            }
+            ++curPos;
+            if ((i = jjnewStateCnt) == (startsAt = 7 - (jjnewStateCnt = startsAt))) {
+                return curPos;
+            }
+            try {
+                curChar = input_stream.readChar();
+            } catch (java.io.IOException e) {
+                return curPos;
+            }
+        }
+    }
+
+    private final int jjStopStringLiteralDfa_2(int pos, long active0) {
+        switch (pos) {
+        case 0:
+            if ((active0 & 0x80000L) != 0L) {
+                return 1;
+            }
+            if ((active0 & 0x50755550070000L) != 0L) {
+                jjmatchedKind = 58;
+                return 6;
+            }
+            return -1;
+        case 1:
+            if ((active0 & 0x105550000000L) != 0L) {
+                return 6;
+            }
+            if ((active0 & 0x50650000070000L) != 0L) {
+                jjmatchedKind = 58;
+                jjmatchedPos = 1;
+                return 6;
+            }
+            return -1;
+        case 2:
+            if ((active0 & 0x50050000000000L) != 0L) {
+                return 6;
+            }
+            if ((active0 & 0x600000070000L) != 0L) {
+                jjmatchedKind = 58;
+                jjmatchedPos = 2;
+                return 6;
+            }
+            return -1;
+        case 3:
+            if ((active0 & 0x50000L) != 0L) {
+                return 6;
+            }
+            if ((active0 & 0x600000020000L) != 0L) {
+                jjmatchedKind = 58;
+                jjmatchedPos = 3;
+                return 6;
+            }
+            return -1;
+        case 4:
+            if ((active0 & 0x400000000000L) != 0L) {
+                jjmatchedKind = 58;
+                jjmatchedPos = 4;
+                return 6;
+            }
+            if ((active0 & 0x200000020000L) != 0L) {
+                return 6;
+            }
+            return -1;
+        case 5:
+            if ((active0 & 0x400000000000L) != 0L) {
+                jjmatchedKind = 58;
+                jjmatchedPos = 5;
+                return 6;
+            }
+            return -1;
+        case 6:
+            if ((active0 & 0x400000000000L) != 0L) {
+                jjmatchedKind = 58;
+                jjmatchedPos = 6;
+                return 6;
+            }
+            return -1;
+        case 7:
+            if ((active0 & 0x400000000000L) != 0L) {
+                jjmatchedKind = 58;
+                jjmatchedPos = 7;
+                return 6;
+            }
+            return -1;
+        case 8:
+            if ((active0 & 0x400000000000L) != 0L) {
+                jjmatchedKind = 58;
+                jjmatchedPos = 8;
+                return 6;
+            }
+            return -1;
+        default:
+            return -1;
+        }
+    }
+
+    private final int jjStartNfa_2(int pos, long active0) {
+        return jjMoveNfa_2(jjStopStringLiteralDfa_2(pos, active0), pos + 1);
+    }
+
+    private int jjMoveStringLiteralDfa0_2() {
+        switch (curChar) {
+        case 33:
+            jjmatchedKind = 39;
+            return jjMoveStringLiteralDfa1_2(0x2000000000L);
+        case 37:
+            return jjStopAtPos(0, 53);
+        case 38:
+            return jjMoveStringLiteralDfa1_2(0x20000000000L);
+        case 40:
+            return jjStopAtPos(0, 20);
+        case 41:
+            return jjStopAtPos(0, 21);
+        case 42:
+            return jjStopAtPos(0, 47);
+        case 43:
+            jjmatchedKind = 48;
+            return jjMoveStringLiteralDfa1_2(0x80000000000000L);
+        case 44:
+            return jjStopAtPos(0, 25);
+        case 45:
+            jjmatchedKind = 49;
+            return jjMoveStringLiteralDfa1_2(0x200000000000000L);
+        case 46:
+            return jjStartNfaWithStates_2(0, 19, 1);
+        case 47:
+            return jjStopAtPos(0, 51);
+        case 58:
+            return jjStopAtPos(0, 24);
+        case 59:
+            return jjStopAtPos(0, 26);
+        case 60:
+            jjmatchedKind = 29;
+            return jjMoveStringLiteralDfa1_2(0x200000000L);
+        case 61:
+            jjmatchedKind = 56;
+            return jjMoveStringLiteralDfa1_2(0x800000000L);
+        case 62:
+            jjmatchedKind = 27;
+            return jjMoveStringLiteralDfa1_2(0x80000000L);
+        case 63:
+            return jjStopAtPos(0, 50);
+        case 91:
+            return jjStopAtPos(0, 22);
+        case 93:
+            return jjStopAtPos(0, 23);
+        case 97:
+            return jjMoveStringLiteralDfa1_2(0x40000000000L);
+        case 100:
+            return jjMoveStringLiteralDfa1_2(0x10000000000000L);
+        case 101:
+            return jjMoveStringLiteralDfa1_2(0x201000000000L);
+        case 102:
+            return jjMoveStringLiteralDfa1_2(0x20000L);
+        case 103:
+            return jjMoveStringLiteralDfa1_2(0x110000000L);
+        case 105:
+            return jjMoveStringLiteralDfa1_2(0x400000000000L);
+        case 108:
+            return jjMoveStringLiteralDfa1_2(0x440000000L);
+        case 109:
+            return jjMoveStringLiteralDfa1_2(0x40000000000000L);
+        case 110:
+            return jjMoveStringLiteralDfa1_2(0x14000040000L);
+        case 111:
+            return jjMoveStringLiteralDfa1_2(0x100000000000L);
+        case 116:
+            return jjMoveStringLiteralDfa1_2(0x10000L);
+        case 123:
+            return jjStopAtPos(0, 9);
+        case 124:
+            return jjMoveStringLiteralDfa1_2(0x80000000000L);
+        case 125:
+            return jjStopAtPos(0, 10);
+        default:
+            return jjMoveNfa_2(0, 0);
+        }
+    }
+
+    private int jjMoveStringLiteralDfa1_2(long active0) {
+        try {
+            curChar = input_stream.readChar();
+        } catch (java.io.IOException e) {
+            jjStopStringLiteralDfa_2(0, active0);
+            return 1;
+        }
+        switch (curChar) {
+        case 38:
+            if ((active0 & 0x20000000000L) != 0L) {
+                return jjStopAtPos(1, 41);
+            }
+            break;
+        case 61:
+            if ((active0 & 0x80000000L) != 0L) {
+                return jjStopAtPos(1, 31);
+            } else if ((active0 & 0x200000000L) != 0L) {
+                return jjStopAtPos(1, 33);
+            } else if ((active0 & 0x800000000L) != 0L) {
+                return jjStopAtPos(1, 35);
+            } else if ((active0 & 0x2000000000L) != 0L) {
+                return jjStopAtPos(1, 37);
+            } else if ((active0 & 0x80000000000000L) != 0L) {
+                return jjStopAtPos(1, 55);
+            }
+            break;
+        case 62:
+            if ((active0 & 0x200000000000000L) != 0L) {
+                return jjStopAtPos(1, 57);
+            }
+            break;
+        case 97:
+            return jjMoveStringLiteralDfa2_2(active0, 0x20000L);
+        case 101:
+            if ((active0 & 0x100000000L) != 0L) {
+                return jjStartNfaWithStates_2(1, 32, 6);
+            } else if ((active0 & 0x400000000L) != 0L) {
+                return jjStartNfaWithStates_2(1, 34, 6);
+            } else if ((active0 & 0x4000000000L) != 0L) {
+                return jjStartNfaWithStates_2(1, 38, 6);
+            }
+            break;
+        case 105:
+            return jjMoveStringLiteralDfa2_2(active0, 0x10000000000000L);
+        case 109:
+            return jjMoveStringLiteralDfa2_2(active0, 0x200000000000L);
+        case 110:
+            return jjMoveStringLiteralDfa2_2(active0, 0x440000000000L);
+        case 111:
+            return jjMoveStringLiteralDfa2_2(active0, 0x40010000000000L);
+        case 113:
+            if ((active0 & 0x1000000000L) != 0L) {
+                return jjStartNfaWithStates_2(1, 36, 6);
+            }
+            break;
+        case 114:
+            if ((active0 & 0x100000000000L) != 0L) {
+                return jjStartNfaWithStates_2(1, 44, 6);
+            }
+            return jjMoveStringLiteralDfa2_2(active0, 0x10000L);
+        case 116:
+            if ((active0 & 0x10000000L) != 0L) {
+                return jjStartNfaWithStates_2(1, 28, 6);
+            } else if ((active0 & 0x40000000L) != 0L) {
+                return jjStartNfaWithStates_2(1, 30, 6);
+            }
+            break;
+        case 117:
+            return jjMoveStringLiteralDfa2_2(active0, 0x40000L);
+        case 124:
+            if ((active0 & 0x80000000000L) != 0L) {
+                return jjStopAtPos(1, 43);
+            }
+            break;
+        default:
+            break;
+        }
+        return jjStartNfa_2(0, active0);
+    }
+
+    private int jjMoveStringLiteralDfa2_2(long old0, long active0) {
+        if (((active0 &= old0)) == 0L) {
+            return jjStartNfa_2(0, old0);
+        }
+        try {
+            curChar = input_stream.readChar();
+        } catch (java.io.IOException e) {
+            jjStopStringLiteralDfa_2(1, active0);
+            return 2;
+        }
+        switch (curChar) {
+        case 100:
+            if ((active0 & 0x40000000000L) != 0L) {
+                return jjStartNfaWithStates_2(2, 42, 6);
+            } else if ((active0 & 0x40000000000000L) != 0L) {
+                return jjStartNfaWithStates_2(2, 54, 6);
+            }
+            break;
+        case 108:
+            return jjMoveStringLiteralDfa3_2(active0, 0x60000L);
+        case 112:
+            return jjMoveStringLiteralDfa3_2(active0, 0x200000000000L);
+        case 115:
+            return jjMoveStringLiteralDfa3_2(active0, 0x400000000000L);
+        case 116:
+            if ((active0 & 0x10000000000L) != 0L) {
+                return jjStartNfaWithStates_2(2, 40, 6);
+            }
+            break;
+        case 117:
+            return jjMoveStringLiteralDfa3_2(active0, 0x10000L);
+        case 118:
+            if ((active0 & 0x10000000000000L) != 0L) {
+                return jjStartNfaWithStates_2(2, 52, 6);
+            }
+            break;
+        default:
+            break;
+        }
+        return jjStartNfa_2(1, active0);
+    }
+
+    private int jjMoveStringLiteralDfa3_2(long old0, long active0) {
+        if (((active0 &= old0)) == 0L) {
+            return jjStartNfa_2(1, old0);
+        }
+        try {
+            curChar = input_stream.readChar();
+        } catch (java.io.IOException e) {
+            jjStopStringLiteralDfa_2(2, active0);
+            return 3;
+        }
+        switch (curChar) {
+        case 101:
+            if ((active0 & 0x10000L) != 0L) {
+                return jjStartNfaWithStates_2(3, 16, 6);
+            }
+            break;
+        case 108:
+            if ((active0 & 0x40000L) != 0L) {
+                return jjStartNfaWithStates_2(3, 18, 6);
+            }
+            break;
+        case 115:
+            return jjMoveStringLiteralDfa4_2(active0, 0x20000L);
+        case 116:
+            return jjMoveStringLiteralDfa4_2(active0, 0x600000000000L);
+        default:
+            break;
+        }
+        return jjStartNfa_2(2, active0);
+    }
+
+    private int jjMoveStringLiteralDfa4_2(long old0, long active0) {
+        if (((active0 &= old0)) == 0L) {
+            return jjStartNfa_2(2, old0);
+        }
+        try {
+            curChar = input_stream.readChar();
+        } catch (java.io.IOException e) {
+            jjStopStringLiteralDfa_2(3, active0);
+            return 4;
+        }
+        switch (curChar) {
+        case 97:
+            return jjMoveStringLiteralDfa5_2(active0, 0x400000000000L);
+        case 101:
+            if ((active0 & 0x20000L) != 0L) {
+                return jjStartNfaWithStates_2(4, 17, 6);
+            }
+            break;
+        case 121:
+            if ((active0 & 0x200000000000L) != 0L) {
+                return jjStartNfaWithStates_2(4, 45, 6);
+            }
+            break;
+        default:
+            break;
+        }
+        return jjStartNfa_2(3, active0);
+    }
+
+    private int jjMoveStringLiteralDfa5_2(long old0, long active0) {
+        if (((active0 &= old0)) == 0L) {
+            return jjStartNfa_2(3, old0);
+        }
+        try {
+            curChar = input_stream.readChar();
+        } catch (java.io.IOException e) {
+            jjStopStringLiteralDfa_2(4, active0);
+            return 5;
+        }
+        switch (curChar) {
+        case 110:
+            return jjMoveStringLiteralDfa6_2(active0, 0x400000000000L);
+        default:
+            break;
+        }
+        return jjStartNfa_2(4, active0);
+    }
+
+    private int jjMoveStringLiteralDfa6_2(long old0, long active0) {
+        if (((active0 &= old0)) == 0L) {
+            return jjStartNfa_2(4, old0);
+        }
+        try {
+            curChar = input_stream.readChar();
+        } catch (java.io.IOException e) {
+            jjStopStringLiteralDfa_2(5, active0);
+            return 6;
+        }
+        switch (curChar) {
+        case 99:
+            return jjMoveStringLiteralDfa7_2(active0, 0x400000000000L);
+        default:
+            break;
+        }
+        return jjStartNfa_2(5, active0);
+    }
+
+    private int jjMoveStringLiteralDfa7_2(long old0, long active0) {
+        if (((active0 &= old0)) == 0L) {
+            return jjStartNfa_2(5, old0);
+        }
+        try {
+            curChar = input_stream.readChar();
+        } catch (java.io.IOException e) {
+            jjStopStringLiteralDfa_2(6, active0);
+            return 7;
+        }
+        switch (curChar) {
+        case 101:
+            return jjMoveStringLiteralDfa8_2(active0, 0x400000000000L);
+        default:
+            break;
+        }
+        return jjStartNfa_2(6, active0);
+    }
+
+    private int jjMoveStringLiteralDfa8_2(long old0, long active0) {
+        if (((active0 &= old0)) == 0L) {
+            return jjStartNfa_2(6, old0);
+        }
+        try {
+            curChar = input_stream.readChar();
+        } catch (java.io.IOException e) {
+            jjStopStringLiteralDfa_2(7, active0);
+            return 8;
+        }
+        switch (curChar) {
+        case 111:
+            return jjMoveStringLiteralDfa9_2(active0, 0x400000000000L);
+        default:
+            break;
+        }
+        return jjStartNfa_2(7, active0);
+    }
+
+    private int jjMoveStringLiteralDfa9_2(long old0, long active0) {
+        if (((active0 &= old0)) == 0L) {
+            return jjStartNfa_2(7, old0);
+        }
+        try {
+            curChar = input_stream.readChar();
+        } catch (java.io.IOException e) {
+            jjStopStringLiteralDfa_2(8, active0);
+            return 9;
+        }
+        switch (curChar) {
+        case 102:
+            if ((active0 & 0x400000000000L) != 0L) {
+                return jjStartNfaWithStates_2(9, 46, 6);
+            }
+            break;
+        default:
+            break;
+        }
+        return jjStartNfa_2(8, active0);
+    }
+
+    private int jjStartNfaWithStates_2(int pos, int kind, int state) {
+        jjmatchedKind = kind;
+        jjmatchedPos = pos;
+        try {
+            curChar = input_stream.readChar();
+        } catch (java.io.IOException e) {
+            return pos + 1;
+        }
+        return jjMoveNfa_2(state, pos + 1);
+    }
+
+    static final long[] jjbitVec3 = { 0x1ff00000fffffffeL, 0xffffffffffffc000L, 0xffffffffL, 0x600000000000000L };
+    static final long[] jjbitVec4 = { 0x0L, 0x0L, 0x0L, 0xff7fffffff7fffffL };
+    static final long[] jjbitVec5 = { 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL };
+    static final long[] jjbitVec6 = { 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffL, 0x0L };
+    static final long[] jjbitVec7 = { 0xffffffffffffffffL, 0xffffffffffffffffL, 0x0L, 0x0L };
+    static final long[] jjbitVec8 = { 0x3fffffffffffL, 0x0L, 0x0L, 0x0L };
+
+    private int jjMoveNfa_2(int startState, int curPos) {
+        int startsAt = 0;
+        jjnewStateCnt = 35;
+        int i = 1;
+        jjstateSet[0] = startState;
+        int kind = 0x7fffffff;
+        for (;;) {
+            if (++jjround == 0x7fffffff) {
+                ReInitRounds();
+            }
+            if (curChar < 64) {
+                long l = 1L << curChar;
+                do {
+                    switch (jjstateSet[--i]) {
+                    case 0:
+                        if ((0x3ff000000000000L & l) != 0L) {
+                            if (kind > 11) {
+                                kind = 11;
+                            }
+                            jjCheckNAddStates(4, 8);
+                        } else if ((0x1800000000L & l) != 0L) {
+                            if (kind > 58) {
+                                kind = 58;
+                            }
+                            jjCheckNAdd(6);
+                        } else if (curChar == 39) {
+                            jjCheckNAddStates(9, 13);
+                        } else if (curChar == 34) {
+                            jjCheckNAddStates(14, 18);
+                        } else if (curChar == 46) {
+                            jjCheckNAdd(1);
+                        }
+                        break;
+                    case 1:
+                        if ((0x3ff000000000000L & l) == 0L) {
+                            break;
+                        }
+                        if (kind > 12) {
+                            kind = 12;
+                        }
+                        jjCheckNAddTwoStates(1, 2);
+                        break;
+                    case 3:
+                        if ((0x280000000000L & l) != 0L) {
+                            jjCheckNAdd(4);
+                        }
+                        break;
+                    case 4:
+                        if ((0x3ff000000000000L & l) == 0L) {
+                            break;
+                        }
+                        if (kind > 12) {
+                            kind = 12;
+                        }
+                        jjCheckNAdd(4);
+                        break;
+                    case 5:
+                        if ((0x1800000000L & l) == 0L) {
+                            break;
+                        }
+                        if (kind > 58) {
+                            kind = 58;
+                        }
+                        jjCheckNAdd(6);
+                        break;
+                    case 6:
+                        if ((0x3ff001000000000L & l) == 0L) {
+                            break;
+                        }
+                        if (kind > 58) {
+                            kind = 58;
+                        }
+                        jjCheckNAdd(6);
+                        break;
+                    case 7:
+                        if ((0x3ff000000000000L & l) == 0L) {
+                            break;
+                        }
+                        if (kind > 11) {
+                            kind = 11;
+                        }
+                        jjCheckNAddStates(4, 8);
+                        break;
+                    case 8:
+                        if ((0x3ff000000000000L & l) == 0L) {
+                            break;
+                        }
+                        if (kind > 11) {
+                            kind = 11;
+                        }
+                        jjCheckNAdd(8);
+                        break;
+                    case 9:
+                        if ((0x3ff000000000000L & l) != 0L) {
+                            jjCheckNAddTwoStates(9, 10);
+                        }
+                        break;
+                    case 10:
+                        if (curChar != 46) {
+                            break;
+                        }
+                        if (kind > 12) {
+                            kind = 12;
+                        }
+                        jjCheckNAddTwoStates(11, 12);
+                        break;
+                    case 11:
+                        if ((0x3ff000000000000L & l) == 0L) {
+                            break;
+                        }
+                        if (kind > 12) {
+                            kind = 12;
+                        }
+                        jjCheckNAddTwoStates(11, 12);
+                        break;
+                    case 13:
+                        if ((0x280000000000L & l) != 0L) {
+                            jjCheckNAdd(14);
+                        }
+                        break;
+                    case 14:
+                        if ((0x3ff000000000000L & l) == 0L) {
+                            break;
+                        }
+                        if (kind > 12) {
+                            kind = 12;
+                        }
+                        jjCheckNAdd(14);
+                        break;
+                    case 15:
+                        if ((0x3ff000000000000L & l) != 0L) {
+                            jjCheckNAddTwoStates(15, 16);
+                        }
+                        break;
+                    case 17:
+                        if ((0x280000000000L & l) != 0L) {
+                            jjCheckNAdd(18);
+                        }
+                        break;
+                    case 18:
+                        if ((0x3ff000000000000L & l) == 0L) {
+                            break;
+                        }
+                        if (kind > 12) {
+                            kind = 12;
+                        }
+                        jjCheckNAdd(18);
+                        break;
+                    case 19:
+                        if (curChar == 34) {
+                            jjCheckNAddStates(14, 18);
+                        }
+                        break;
+                    case 20:
+                        if ((0xfffffffbffffffffL & l) != 0L) {
+                            jjCheckNAddStates(19, 21);
+                        }
+                        break;
+                    case 22:
+                        if (curChar == 34) {
+                            jjCheckNAddStates(19, 21);
+                        }
+                        break;
+                    case 23:
+                        if (curChar == 34 && kind > 14) {
+                            kind = 14;
+                        }
+                        break;
+                    case 24:
+                        if ((0xfffffffbffffffffL & l) != 0L) {
+                            jjCheckNAddTwoStates(24, 25);
+                        }
+                        break;
+                    case 26:
+                        if ((0xfffffffbffffffffL & l) != 0L && kind > 15) {
+                            kind = 15;
+                        }
+                        break;
+                    case 27:
+                        if (curChar == 39) {
+                            jjCheckNAddStates(9, 13);
+                        }
+                        break;
+                    case 28:
+                        if ((0xffffff7fffffffffL & l) != 0L) {
+                            jjCheckNAddStates(22, 24);
+                        }
+                        break;
+                    case 30:
+                        if (curChar == 39) {
+                            jjCheckNAddStates(22, 24);
+                        }
+                        break;
+                    case 31:
+                        if (curChar == 39 && kind > 14) {
+                            kind = 14;
+                        }
+                        break;
+                    case 32:
+                        if ((0xffffff7fffffffffL & l) != 0L) {
+                            jjCheckNAddTwoStates(32, 33);
+                        }
+                        break;
+                    case 34:
+                        if ((0xffffff7fffffffffL & l) != 0L && kind > 15) {
+                            kind = 15;
+                        }
+                        break;
+                    default:
+                        break;
+                    }
+                } while (i != startsAt);
+            } else if (curChar < 128) {
+                long l = 1L << (curChar & 077);
+                do {
+                    switch (jjstateSet[--i]) {
+                    case 0:
+                    case 6:
+                        if ((0x7fffffe87fffffeL & l) == 0L) {
+                            break;
+                        }
+                        if (kind > 58) {
+                            kind = 58;
+                        }
+                        jjCheckNAdd(6);
+                        break;
+                    case 2:
+                        if ((0x2000000020L & l) != 0L) {
+                            jjAddStates(25, 26);
+                        }
+                        break;
+                    case 12:
+                        if ((0x2000000020L & l) != 0L) {
+                            jjAddStates(27, 28);
+                        }
+                        break;
+                    case 16:
+                        if ((0x2000000020L & l) != 0L) {
+                            jjAddStates(29, 30);
+                        }
+                        break;
+                    case 20:
+                        if ((0xffffffffefffffffL & l) != 0L) {
+                            jjCheckNAddStates(19, 21);
+                        }
+                        break;
+                    case 21:
+                        if (curChar == 92) {
+                            jjstateSet[jjnewStateCnt++] = 22;
+                        }
+                        break;
+                    case 22:
+                        if (curChar == 92) {
+                            jjCheckNAddStates(19, 21);
+                        }
+                        break;
+                    case 24:
+                        if ((0xffffffffefffffffL & l) != 0L) {
+                            jjAddStates(31, 32);
+                        }
+                        break;
+                    case 25:
+                        if (curChar == 92) {
+                            jjstateSet[jjnewStateCnt++] = 26;
+                        }
+                        break;
+                    case 26:
+                    case 34:
+                        if ((0xffffffffefffffffL & l) != 0L && kind > 15) {
+                            kind = 15;
+                        }
+                        break;
+                    case 28:
+                        if ((0xffffffffefffffffL & l) != 0L) {
+                            jjCheckNAddStates(22, 24);
+                        }
+                        break;
+                    case 29:
+                        if (curChar == 92) {
+                            jjstateSet[jjnewStateCnt++] = 30;
+                        }
+                        break;
+                    case 30:
+                        if (curChar == 92) {
+                            jjCheckNAddStates(22, 24);
+                        }
+                        break;
+                    case 32:
+                        if ((0xffffffffefffffffL & l) != 0L) {
+                            jjAddStates(33, 34);
+                        }
+                        break;
+                    case 33:
+                        if (curChar == 92) {
+                            jjstateSet[jjnewStateCnt++] = 34;
+                        }
+                        break;
+                    default:
+                        break;
+                    }
+                } while (i != startsAt);
+            } else {
+                int hiByte = curChar >> 8;
+                int i1 = hiByte >> 6;
+                long l1 = 1L << (hiByte & 077);
+                int i2 = (curChar & 0xff) >> 6;
+                long l2 = 1L << (curChar & 077);
+                do {
+                    switch (jjstateSet[--i]) {
+                    case 0:
+                    case 6:
+                        if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) {
+                            break;
+                        }
+                        if (kind > 58) {
+                            kind = 58;
+                        }
+                        jjCheckNAdd(6);
+                        break;
+                    case 20:
+                        if (jjCanMove_0(hiByte, i1, i2, l1, l2)) {
+                            jjAddStates(19, 21);
+                        }
+                        break;
+                    case 24:
+                        if (jjCanMove_0(hiByte, i1, i2, l1, l2)) {
+                            jjAddStates(31, 32);
+                        }
+                        break;
+                    case 26:
+                    case 34:
+                        if (jjCanMove_0(hiByte, i1, i2, l1, l2) && kind > 15) {
+                            kind = 15;
+                        }
+                        break;
+                    case 28:
+                        if (jjCanMove_0(hiByte, i1, i2, l1, l2)) {
+                            jjAddStates(22, 24);
+                        }
+                        break;
+                    case 32:
+                        if (jjCanMove_0(hiByte, i1, i2, l1, l2)) {
+                            jjAddStates(33, 34);
+                        }
+                        break;
+                    default:
+                        break;
+                    }
+                } while (i != startsAt);
+            }
+            if (kind != 0x7fffffff) {
+                jjmatchedKind = kind;
+                jjmatchedPos = curPos;
+                kind = 0x7fffffff;
+            }
+            ++curPos;
+            if ((i = jjnewStateCnt) == (startsAt = 35 - (jjnewStateCnt = startsAt))) {
+                return curPos;
+            }
+            try {
+                curChar = input_stream.readChar();
+            } catch (java.io.IOException e) {
+                return curPos;
+            }
+        }
+    }
+
+    private final int jjStopStringLiteralDfa_1(int pos, long active0) {
+        switch (pos) {
+        case 0:
+            if ((active0 & 0x80000L) != 0L) {
+                return 1;
+            }
+            if ((active0 & 0x50755550070000L) != 0L) {
+                jjmatchedKind = 58;
+                return 6;
+            }
+            return -1;
+        case 1:
+            if ((active0 & 0x105550000000L) != 0L) {
+                return 6;
+            }
+            if ((active0 & 0x50650000070000L) != 0L) {
+                jjmatchedKind = 58;
+                jjmatchedPos = 1;
+                return 6;
+            }
+            return -1;
+        case 2:
+            if ((active0 & 0x50050000000000L) != 0L) {
+                return 6;
+            }
+            if ((active0 & 0x600000070000L) != 0L) {
+                jjmatchedKind = 58;
+                jjmatchedPos = 2;
+                return 6;
+            }
+            return -1;
+        case 3:
+            if ((active0 & 0x50000L) != 0L) {
+                return 6;
+            }
+            if ((active0 & 0x600000020000L) != 0L) {
+                jjmatchedKind = 58;
+                jjmatchedPos = 3;
+                return 6;
+            }
+            return -1;
+        case 4:
+            if ((active0 & 0x400000000000L) != 0L) {
+                jjmatchedKind = 58;
+                jjmatchedPos = 4;
+                return 6;
+            }
+            if ((active0 & 0x200000020000L) != 0L) {
+                return 6;
+            }
+            return -1;
+        case 5:
+            if ((active0 & 0x400000000000L) != 0L) {
+                jjmatchedKind = 58;
+                jjmatchedPos = 5;
+                return 6;
+            }
+            return -1;
+        case 6:
+            if ((active0 & 0x400000000000L) != 0L) {
+                jjmatchedKind = 58;
+                jjmatchedPos = 6;
+                return 6;
+            }
+            return -1;
+        case 7:
+            if ((active0 & 0x400000000000L) != 0L) {
+                jjmatchedKind = 58;
+                jjmatchedPos = 7;
+                return 6;
+            }
+            return -1;
+        case 8:
+            if ((active0 & 0x400000000000L) != 0L) {
+                jjmatchedKind = 58;
+                jjmatchedPos = 8;
+                return 6;
+            }
+            return -1;
+        default:
+            return -1;
+        }
+    }
+
+    private final int jjStartNfa_1(int pos, long active0) {
+        return jjMoveNfa_1(jjStopStringLiteralDfa_1(pos, active0), pos + 1);
+    }
+
+    private int jjMoveStringLiteralDfa0_1() {
+        switch (curChar) {
+        case 33:
+            jjmatchedKind = 39;
+            return jjMoveStringLiteralDfa1_1(0x2000000000L);
+        case 37:
+            return jjStopAtPos(0, 53);
+        case 38:
+            return jjMoveStringLiteralDfa1_1(0x20000000000L);
+        case 40:
+            return jjStopAtPos(0, 20);
+        case 41:
+            return jjStopAtPos(0, 21);
+        case 42:
+            return jjStopAtPos(0, 47);
+        case 43:
+            jjmatchedKind = 48;
+            return jjMoveStringLiteralDfa1_1(0x80000000000000L);
+        case 44:
+            return jjStopAtPos(0, 25);
+        case 45:
+            jjmatchedKind = 49;
+            return jjMoveStringLiteralDfa1_1(0x200000000000000L);
+        case 46:
+            return jjStartNfaWithStates_1(0, 19, 1);
+        case 47:
+            return jjStopAtPos(0, 51);
+        case 58:
+            return jjStopAtPos(0, 24);
+        case 59:
+            return jjStopAtPos(0, 26);
+        case 60:
+            jjmatchedKind = 29;
+            return jjMoveStringLiteralDfa1_1(0x200000000L);
+        case 61:
+            jjmatchedKind = 56;
+            return jjMoveStringLiteralDfa1_1(0x800000000L);
+        case 62:
+            jjmatchedKind = 27;
+            return jjMoveStringLiteralDfa1_1(0x80000000L);
+        case 63:
+            return jjStopAtPos(0, 50);
+        case 91:
+            return jjStopAtPos(0, 22);
+        case 93:
+            return jjStopAtPos(0, 23);
+        case 97:
+            return jjMoveStringLiteralDfa1_1(0x40000000000L);
+        case 100:
+            return jjMoveStringLiteralDfa1_1(0x10000000000000L);
+        case 101:
+            return jjMoveStringLiteralDfa1_1(0x201000000000L);
+        case 102:
+            return jjMoveStringLiteralDfa1_1(0x20000L);
+        case 103:
+            return jjMoveStringLiteralDfa1_1(0x110000000L);
+        case 105:
+            return jjMoveStringLiteralDfa1_1(0x400000000000L);
+        case 108:
+            return jjMoveStringLiteralDfa1_1(0x440000000L);
+        case 109:
+            return jjMoveStringLiteralDfa1_1(0x40000000000000L);
+        case 110:
+            return jjMoveStringLiteralDfa1_1(0x14000040000L);
+        case 111:
+            return jjMoveStringLiteralDfa1_1(0x100000000000L);
+        case 116:
+            return jjMoveStringLiteralDfa1_1(0x10000L);
+        case 123:
+            return jjStopAtPos(0, 9);
+        case 124:
+            return jjMoveStringLiteralDfa1_1(0x80000000000L);
+        case 125:
+            return jjStopAtPos(0, 10);
+        default:
+            return jjMoveNfa_1(0, 0);
+        }
+    }
+
+    private int jjMoveStringLiteralDfa1_1(long active0) {
+        try {
+            curChar = input_stream.readChar();
+        } catch (java.io.IOException e) {
+            jjStopStringLiteralDfa_1(0, active0);
+            return 1;
+        }
+        switch (curChar) {
+        case 38:
+            if ((active0 & 0x20000000000L) != 0L) {
+                return jjStopAtPos(1, 41);
+            }
+            break;
+        case 61:
+            if ((active0 & 0x80000000L) != 0L) {
+                return jjStopAtPos(1, 31);
+            } else if ((active0 & 0x200000000L) != 0L) {
+                return jjStopAtPos(1, 33);
+            } else if ((active0 & 0x800000000L) != 0L) {
+                return jjStopAtPos(1, 35);
+            } else if ((active0 & 0x2000000000L) != 0L) {
+                return jjStopAtPos(1, 37);
+            } else if ((active0 & 0x80000000000000L) != 0L) {
+                return jjStopAtPos(1, 55);
+            }
+            break;
+        case 62:
+            if ((active0 & 0x200000000000000L) != 0L) {
+                return jjStopAtPos(1, 57);
+            }
+            break;
+        case 97:
+            return jjMoveStringLiteralDfa2_1(active0, 0x20000L);
+        case 101:
+            if ((active0 & 0x100000000L) != 0L) {
+                return jjStartNfaWithStates_1(1, 32, 6);
+            } else if ((active0 & 0x400000000L) != 0L) {
+                return jjStartNfaWithStates_1(1, 34, 6);
+            } else if ((active0 & 0x4000000000L) != 0L) {
+                return jjStartNfaWithStates_1(1, 38, 6);
+            }
+            break;
+        case 105:
+            return jjMoveStringLiteralDfa2_1(active0, 0x10000000000000L);
+        case 109:
+            return jjMoveStringLiteralDfa2_1(active0, 0x200000000000L);
+        case 110:
+            return jjMoveStringLiteralDfa2_1(active0, 0x440000000000L);
+        case 111:
+            return jjMoveStringLiteralDfa2_1(active0, 0x40010000000000L);
+        case 113:
+            if ((active0 & 0x1000000000L) != 0L) {
+                return jjStartNfaWithStates_1(1, 36, 6);
+            }
+            break;
+        case 114:
+            if ((active0 & 0x100000000000L) != 0L) {
+                return jjStartNfaWithStates_1(1, 44, 6);
+            }
+            return jjMoveStringLiteralDfa2_1(active0, 0x10000L);
+        case 116:
+            if ((active0 & 0x10000000L) != 0L) {
+                return jjStartNfaWithStates_1(1, 28, 6);
+            } else if ((active0 & 0x40000000L) != 0L) {
+                return jjStartNfaWithStates_1(1, 30, 6);
+            }
+            break;
+        case 117:
+            return jjMoveStringLiteralDfa2_1(active0, 0x40000L);
+        case 124:
+            if ((active0 & 0x80000000000L) != 0L) {
+                return jjStopAtPos(1, 43);
+            }
+            break;
+        default:
+            break;
+        }
+        return jjStartNfa_1(0, active0);
+    }
+
+    private int jjMoveStringLiteralDfa2_1(long old0, long active0) {
+        if (((active0 &= old0)) == 0L) {
+            return jjStartNfa_1(0, old0);
+        }
+        try {
+            curChar = input_stream.readChar();
+        } catch (java.io.IOException e) {
+            jjStopStringLiteralDfa_1(1, active0);
+            return 2;
+        }
+        switch (curChar) {
+        case 100:
+            if ((active0 & 0x40000000000L) != 0L) {
+                return jjStartNfaWithStates_1(2, 42, 6);
+            } else if ((active0 & 0x40000000000000L) != 0L) {
+                return jjStartNfaWithStates_1(2, 54, 6);
+            }
+            break;
+        case 108:
+            return jjMoveStringLiteralDfa3_1(active0, 0x60000L);
+        case 112:
+            return jjMoveStringLiteralDfa3_1(active0, 0x200000000000L);
+        case 115:
+            return jjMoveStringLiteralDfa3_1(active0, 0x400000000000L);
+        case 116:
+            if ((active0 & 0x10000000000L) != 0L) {
+                return jjStartNfaWithStates_1(2, 40, 6);
+            }
+            break;
+        case 117:
+            return jjMoveStringLiteralDfa3_1(active0, 0x10000L);
+        case 118:
+            if ((active0 & 0x10000000000000L) != 0L) {
+                return jjStartNfaWithStates_1(2, 52, 6);
+            }
+            break;
+        default:
+            break;
+        }
+        return jjStartNfa_1(1, active0);
+    }
+
+    private int jjMoveStringLiteralDfa3_1(long old0, long active0) {
+        if (((active0 &= old0)) == 0L) {
+            return jjStartNfa_1(1, old0);
+        }
+        try {
+            curChar = input_stream.readChar();
+        } catch (java.io.IOException e) {
+            jjStopStringLiteralDfa_1(2, active0);
+            return 3;
+        }
+        switch (curChar) {
+        case 101:
+            if ((active0 & 0x10000L) != 0L) {
+                return jjStartNfaWithStates_1(3, 16, 6);
+            }
+            break;
+        case 108:
+            if ((active0 & 0x40000L) != 0L) {
+                return jjStartNfaWithStates_1(3, 18, 6);
+            }
+            break;
+        case 115:
+            return jjMoveStringLiteralDfa4_1(active0, 0x20000L);
+        case 116:
+            return jjMoveStringLiteralDfa4_1(active0, 0x600000000000L);
+        default:
+            break;
+        }
+        return jjStartNfa_1(2, active0);
+    }
+
+    private int jjMoveStringLiteralDfa4_1(long old0, long active0) {
+        if (((active0 &= old0)) == 0L) {
+            return jjStartNfa_1(2, old0);
+        }
+        try {
+            curChar = input_stream.readChar();
+        } catch (java.io.IOException e) {
+            jjStopStringLiteralDfa_1(3, active0);
+            return 4;
+        }
+        switch (curChar) {
+        case 97:
+            return jjMoveStringLiteralDfa5_1(active0, 0x400000000000L);
+        case 101:
+            if ((active0 & 0x20000L) != 0L) {
+                return jjStartNfaWithStates_1(4, 17, 6);
+            }
+            break;
+        case 121:
+            if ((active0 & 0x200000000000L) != 0L) {
+                return jjStartNfaWithStates_1(4, 45, 6);
+            }
+            break;
+        default:
+            break;
+        }
+        return jjStartNfa_1(3, active0);
+    }
+
+    private int jjMoveStringLiteralDfa5_1(long old0, long active0) {
+        if (((active0 &= old0)) == 0L) {
+            return jjStartNfa_1(3, old0);
+        }
+        try {
+            curChar = input_stream.readChar();
+        } catch (java.io.IOException e) {
+            jjStopStringLiteralDfa_1(4, active0);
+            return 5;
+        }
+        switch (curChar) {
+        case 110:
+            return jjMoveStringLiteralDfa6_1(active0, 0x400000000000L);
+        default:
+            break;
+        }
+        return jjStartNfa_1(4, active0);
+    }
+
+    private int jjMoveStringLiteralDfa6_1(long old0, long active0) {
+        if (((active0 &= old0)) == 0L) {
+            return jjStartNfa_1(4, old0);
+        }
+        try {
+            curChar = input_stream.readChar();
+        } catch (java.io.IOException e) {
+            jjStopStringLiteralDfa_1(5, active0);
+            return 6;
+        }
+        switch (curChar) {
+        case 99:
+            return jjMoveStringLiteralDfa7_1(active0, 0x400000000000L);
+        default:
+            break;
+        }
+        return jjStartNfa_1(5, active0);
+    }
+
+    private int jjMoveStringLiteralDfa7_1(long old0, long active0) {
+        if (((active0 &= old0)) == 0L) {
+            return jjStartNfa_1(5, old0);
+        }
+        try {
+            curChar = input_stream.readChar();
+        } catch (java.io.IOException e) {
+            jjStopStringLiteralDfa_1(6, active0);
+            return 7;
+        }
+        switch (curChar) {
+        case 101:
+            return jjMoveStringLiteralDfa8_1(active0, 0x400000000000L);
+        default:
+            break;
+        }
+        return jjStartNfa_1(6, active0);
+    }
+
+    private int jjMoveStringLiteralDfa8_1(long old0, long active0) {
+        if (((active0 &= old0)) == 0L) {
+            return jjStartNfa_1(6, old0);
+        }
+        try {
+            curChar = input_stream.readChar();
+        } catch (java.io.IOException e) {
+            jjStopStringLiteralDfa_1(7, active0);
+            return 8;
+        }
+        switch (curChar) {
+        case 111:
+            return jjMoveStringLiteralDfa9_1(active0, 0x400000000000L);
+        default:
+            break;
+        }
+        return jjStartNfa_1(7, active0);
+    }
+
+    private int jjMoveStringLiteralDfa9_1(long old0, long active0) {
+        if (((active0 &= old0)) == 0L) {
+            return jjStartNfa_1(7, old0);
+        }
+        try {
+            curChar = input_stream.readChar();
+        } catch (java.io.IOException e) {
+            jjStopStringLiteralDfa_1(8, active0);
+            return 9;
+        }
+        switch (curChar) {
+        case 102:
+            if ((active0 & 0x400000000000L) != 0L) {
+                return jjStartNfaWithStates_1(9, 46, 6);
+            }
+            break;
+        default:
+            break;
+        }
+        return jjStartNfa_1(8, active0);
+    }
+
+    private int jjStartNfaWithStates_1(int pos, int kind, int state) {
+        jjmatchedKind = kind;
+        jjmatchedPos = pos;
+        try {
+            curChar = input_stream.readChar();
+        } catch (java.io.IOException e) {
+            return pos + 1;
+        }
+        return jjMoveNfa_1(state, pos + 1);
+    }
+
+    private int jjMoveNfa_1(int startState, int curPos) {
+        int startsAt = 0;
+        jjnewStateCnt = 35;
+        int i = 1;
+        jjstateSet[0] = startState;
+        int kind = 0x7fffffff;
+        for (;;) {
+            if (++jjround == 0x7fffffff) {
+                ReInitRounds();
+            }
+            if (curChar < 64) {
+                long l = 1L << curChar;
+                do {
+                    switch (jjstateSet[--i]) {
+                    case 0:
+                        if ((0x3ff000000000000L & l) != 0L) {
+                            if (kind > 11) {
+                                kind = 11;
+                            }
+                            jjCheckNAddStates(4, 8);
+                        } else if ((0x1800000000L & l) != 0L) {
+                            if (kind > 58) {
+                                kind = 58;
+                            }
+                            jjCheckNAdd(6);
+                        } else if (curChar == 39) {
+                            jjCheckNAddStates(9, 13);
+                        } else if (curChar == 34) {
+                            jjCheckNAddStates(14, 18);
+                        } else if (curChar == 46) {
+                            jjCheckNAdd(1);
+                        }
+                        break;
+                    case 1:
+                        if ((0x3ff000000000000L & l) == 0L) {
+                            break;
+                        }
+                        if (kind > 12) {
+                            kind = 12;
+                        }
+                        jjCheckNAddTwoStates(1, 2);
+                        break;
+                    case 3:
+                        if ((0x280000000000L & l) != 0L) {
+                            jjCheckNAdd(4);
+                        }
+                        break;
+                    case 4:
+                        if ((0x3ff000000000000L & l) == 0L) {
+                            break;
+                        }
+                        if (kind > 12) {
+                            kind = 12;
+                        }
+                        jjCheckNAdd(4);
+                        break;
+                    case 5:
+                        if ((0x1800000000L & l) == 0L) {
+                            break;
+                        }
+                        if (kind > 58) {
+                            kind = 58;
+                        }
+                        jjCheckNAdd(6);
+                        break;
+                    case 6:
+                        if ((0x3ff001000000000L & l) == 0L) {
+                            break;
+                        }
+                        if (kind > 58) {
+                            kind = 58;
+                        }
+                        jjCheckNAdd(6);
+                        break;
+                    case 7:
+                        if ((0x3ff000000000000L & l) == 0L) {
+                            break;
+                        }
+                        if (kind > 11) {
+                            kind = 11;
+                        }
+                        jjCheckNAddStates(4, 8);
+                        break;
+                    case 8:
+                        if ((0x3ff000000000000L & l) == 0L) {
+                            break;
+                        }
+                        if (kind > 11) {
+                            kind = 11;
+                        }
+                        jjCheckNAdd(8);
+                        break;
+                    case 9:
+                        if ((0x3ff000000000000L & l) != 0L) {
+                            jjCheckNAddTwoStates(9, 10);
+                        }
+                        break;
+                    case 10:
+                        if (curChar != 46) {
+                            break;
+                        }
+                        if (kind > 12) {
+                            kind = 12;
+                        }
+                        jjCheckNAddTwoStates(11, 12);
+                        break;
+                    case 11:
+                        if ((0x3ff000000000000L & l) == 0L) {
+                            break;
+                        }
+                        if (kind > 12) {
+                            kind = 12;
+                        }
+                        jjCheckNAddTwoStates(11, 12);
+                        break;
+                    case 13:
+                        if ((0x280000000000L & l) != 0L) {
+                            jjCheckNAdd(14);
+                        }
+                        break;
+                    case 14:
+                        if ((0x3ff000000000000L & l) == 0L) {
+                            break;
+                        }
+                        if (kind > 12) {
+                            kind = 12;
+                        }
+                        jjCheckNAdd(14);
+                        break;
+                    case 15:
+                        if ((0x3ff000000000000L & l) != 0L) {
+                            jjCheckNAddTwoStates(15, 16);
+                        }
+                        break;
+                    case 17:
+                        if ((0x280000000000L & l) != 0L) {
+                            jjCheckNAdd(18);
+                        }
+                        break;
+                    case 18:
+                        if ((0x3ff000000000000L & l) == 0L) {
+                            break;
+                        }
+                        if (kind > 12) {
+                            kind = 12;
+                        }
+                        jjCheckNAdd(18);
+                        break;
+                    case 19:
+                        if (curChar == 34) {
+                            jjCheckNAddStates(14, 18);
+                        }
+                        break;
+                    case 20:
+                        if ((0xfffffffbffffffffL & l) != 0L) {
+                            jjCheckNAddStates(19, 21);
+                        }
+                        break;
+                    case 22:
+                        if (curChar == 34) {
+                            jjCheckNAddStates(19, 21);
+                        }
+                        break;
+                    case 23:
+                        if (curChar == 34 && kind > 14) {
+                            kind = 14;
+                        }
+                        break;
+                    case 24:
+                        if ((0xfffffffbffffffffL & l) != 0L) {
+                            jjCheckNAddTwoStates(24, 25);
+                        }
+                        break;
+                    case 26:
+                        if ((0xfffffffbffffffffL & l) != 0L && kind > 15) {
+                            kind = 15;
+                        }
+                        break;
+                    case 27:
+                        if (curChar == 39) {
+                            jjCheckNAddStates(9, 13);
+                        }
+                        break;
+                    case 28:
+                        if ((0xffffff7fffffffffL & l) != 0L) {
+                            jjCheckNAddStates(22, 24);
+                        }
+                        break;
+                    case 30:
+                        if (curChar == 39) {
+                            jjCheckNAddStates(22, 24);
+                        }
+                        break;
+                    case 31:
+                        if (curChar == 39 && kind > 14) {
+                            kind = 14;
+                        }
+                        break;
+                    case 32:
+                        if ((0xffffff7fffffffffL & l) != 0L) {
+                            jjCheckNAddTwoStates(32, 33);
+                        }
+                        break;
+                    case 34:
+                        if ((0xffffff7fffffffffL & l) != 0L && kind > 15) {
+                            kind = 15;
+                        }
+                        break;
+                    default:
+                        break;
+                    }
+                } while (i != startsAt);
+            } else if (curChar < 128) {
+                long l = 1L << (curChar & 077);
+                do {
+                    switch (jjstateSet[--i]) {
+                    case 0:
+                    case 6:
+                        if ((0x7fffffe87fffffeL & l) == 0L) {
+                            break;
+                        }
+                        if (kind > 58) {
+                            kind = 58;
+                        }
+                        jjCheckNAdd(6);
+                        break;
+                    case 2:
+                        if ((0x2000000020L & l) != 0L) {
+                            jjAddStates(25, 26);
+                        }
+                        break;
+                    case 12:
+                        if ((0x2000000020L & l) != 0L) {
+                            jjAddStates(27, 28);
+                        }
+                        break;
+                    case 16:
+                        if ((0x2000000020L & l) != 0L) {
+                            jjAddStates(29, 30);
+                        }
+                        break;
+                    case 20:
+                        if ((0xffffffffefffffffL & l) != 0L) {
+                            jjCheckNAddStates(19, 21);
+                        }
+                        break;
+                    case 21:
+                        if (curChar == 92) {
+                            jjstateSet[jjnewStateCnt++] = 22;
+                        }
+                        break;
+                    case 22:
+                        if (curChar == 92) {
+                            jjCheckNAddStates(19, 21);
+                        }
+                        break;
+                    case 24:
+                        if ((0xffffffffefffffffL & l) != 0L) {
+                            jjAddStates(31, 32);
+                        }
+                        break;
+                    case 25:
+                        if (curChar == 92) {
+                            jjstateSet[jjnewStateCnt++] = 26;
+                        }
+                        break;
+                    case 26:
+                    case 34:
+                        if ((0xffffffffefffffffL & l) != 0L && kind > 15) {
+                            kind = 15;
+                        }
+                        break;
+                    case 28:
+                        if ((0xffffffffefffffffL & l) != 0L) {
+                            jjCheckNAddStates(22, 24);
+                        }
+                        break;
+                    case 29:
+                        if (curChar == 92) {
+                            jjstateSet[jjnewStateCnt++] = 30;
+                        }
+                        break;
+                    case 30:
+                        if (curChar == 92) {
+                            jjCheckNAddStates(22, 24);
+                        }
+                        break;
+                    case 32:
+                        if ((0xffffffffefffffffL & l) != 0L) {
+                            jjAddStates(33, 34);
+                        }
+                        break;
+                    case 33:
+                        if (curChar == 92) {
+                            jjstateSet[jjnewStateCnt++] = 34;
+                        }
+                        break;
+                    default:
+                        break;
+                    }
+                } while (i != startsAt);
+            } else {
+                int hiByte = curChar >> 8;
+                int i1 = hiByte >> 6;
+                long l1 = 1L << (hiByte & 077);
+                int i2 = (curChar & 0xff) >> 6;
+                long l2 = 1L << (curChar & 077);
+                do {
+                    switch (jjstateSet[--i]) {
+                    case 0:
+                    case 6:
+                        if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) {
+                            break;
+                        }
+                        if (kind > 58) {
+                            kind = 58;
+                        }
+                        jjCheckNAdd(6);
+                        break;
+                    case 20:
+                        if (jjCanMove_0(hiByte, i1, i2, l1, l2)) {
+                            jjAddStates(19, 21);
+                        }
+                        break;
+                    case 24:
+                        if (jjCanMove_0(hiByte, i1, i2, l1, l2)) {
+                            jjAddStates(31, 32);
+                        }
+                        break;
+                    case 26:
+                    case 34:
+                        if (jjCanMove_0(hiByte, i1, i2, l1, l2) && kind > 15) {
+                            kind = 15;
+                        }
+                        break;
+                    case 28:
+                        if (jjCanMove_0(hiByte, i1, i2, l1, l2)) {
+                            jjAddStates(22, 24);
+                        }
+                        break;
+                    case 32:
+                        if (jjCanMove_0(hiByte, i1, i2, l1, l2)) {
+                            jjAddStates(33, 34);
+                        }
+                        break;
+                    default:
+                        break;
+                    }
+                } while (i != startsAt);
+            }
+            if (kind != 0x7fffffff) {
+                jjmatchedKind = kind;
+                jjmatchedPos = curPos;
+                kind = 0x7fffffff;
+            }
+            ++curPos;
+            if ((i = jjnewStateCnt) == (startsAt = 35 - (jjnewStateCnt = startsAt))) {
+                return curPos;
+            }
+            try {
+                curChar = input_stream.readChar();
+            } catch (java.io.IOException e) {
+                return curPos;
+            }
+        }
+    }
+
+    static final int[] jjnextStates = { 0, 1, 3, 5, 8, 9, 10, 15, 16, 28, 29, 31, 32, 33, 20, 21, 23, 24, 25, 20, 21, 23, 28, 29, 31, 3, 4, 13, 14, 17, 18, 24,
+            25, 32, 33, };
+
+    private static final boolean jjCanMove_0(int hiByte, int i1, int i2, long l1, long l2) {
+        switch (hiByte) {
+        case 0:
+            return ((jjbitVec2[i2] & l2) != 0L);
+        default:
+            if ((jjbitVec0[i1] & l1) != 0L) {
+                return true;
+            }
+            return false;
+        }
+    }
+
+    private static final boolean jjCanMove_1(int hiByte, int i1, int i2, long l1, long l2) {
+        switch (hiByte) {
+        case 0:
+            return ((jjbitVec4[i2] & l2) != 0L);
+        case 48:
+            return ((jjbitVec5[i2] & l2) != 0L);
+        case 49:
+            return ((jjbitVec6[i2] & l2) != 0L);
+        case 51:
+            return ((jjbitVec7[i2] & l2) != 0L);
+        case 61:
+            return ((jjbitVec8[i2] & l2) != 0L);
+        default:
+            if ((jjbitVec3[i1] & l1) != 0L) {
+                return true;
+            }
+            return false;
+        }
+    }
+
+    /** Token literal values. */
+    public static final String[] jjstrLiteralImages = { "", null, "\44\173", "\43\173", null, null, null, null, null, "\173", "\175", null, null, null, null,
+            null, "\164\162\165\145", "\146\141\154\163\145", "\156\165\154\154", "\56", "\50", "\51", "\133", "\135", "\72", "\54", "\73", "\76", "\147\164",
+            "\74", "\154\164", "\76\75", "\147\145", "\74\75", "\154\145", "\75\75", "\145\161", "\41\75", "\156\145", "\41", "\156\157\164", "\46\46",
+            "\141\156\144", "\174\174", "\157\162", "\145\155\160\164\171", "\151\156\163\164\141\156\143\145\157\146", "\52", "\53", "\55", "\77", "\57",
+            "\144\151\166", "\45", "\155\157\144", "\53\75", "\75", "\55\76", null, null, null, null, null, };
+
+    /** Lexer state names. */
+    public static final String[] lexStateNames = { "DEFAULT", "IN_EXPRESSION", "IN_MAP", };
+
+    /** Lex State array. */
+    public static final int[] jjnewLexState = { -1, -1, 1, 1, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, };
+    static final long[] jjtoToken = { 0x47ffffffffffde0fL, };
+    static final long[] jjtoSkip = { 0x1f0L, };
+    protected SimpleCharStream input_stream;
+    private final int[] jjrounds = new int[35];
+    private final int[] jjstateSet = new int[70];
+    private final StringBuilder jjimage = new StringBuilder();
+    private StringBuilder image = jjimage;
+    private int jjimageLen;
+    private int lengthOfMatch;
+    protected char curChar;
+
+    /** Constructor. */
+    public ELParserTokenManager(SimpleCharStream stream) {
+        if (SimpleCharStream.staticFlag) {
+            throw new Error("ERROR: Cannot use a static CharStream class with a non-static lexical analyzer.");
+        }
+        input_stream = stream;
+    }
+
+    /** Constructor. */
+    public ELParserTokenManager(SimpleCharStream stream, int lexState) {
+        this(stream);
+        SwitchTo(lexState);
+    }
+
+    /** Reinitialise parser. */
+    public void ReInit(SimpleCharStream stream) {
+        jjmatchedPos = jjnewStateCnt = 0;
+        curLexState = defaultLexState;
+        input_stream = stream;
+        ReInitRounds();
+    }
+
+    private void ReInitRounds() {
+        int i;
+        jjround = 0x80000001;
+        for (i = 35; i-- > 0;) {
+            jjrounds[i] = 0x80000000;
+        }
+    }
+
+    /** Reinitialise parser. */
+    public void ReInit(SimpleCharStream stream, int lexState) {
+        ReInit(stream);
+        SwitchTo(lexState);
+    }
+
+    /** Switch to specified lex state. */
+    public void SwitchTo(int lexState) {
+        if (lexState >= 3 || lexState < 0) {
+            throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", TokenMgrError.INVALID_LEXICAL_STATE);
+        } else {
+            curLexState = lexState;
+        }
+    }
+
+    protected Token jjFillToken() {
+        final Token t;
+        final String curTokenImage;
+        final int beginLine;
+        final int endLine;
+        final int beginColumn;
+        final int endColumn;
+        String im = jjstrLiteralImages[jjmatchedKind];
+        curTokenImage = (im == null) ? input_stream.GetImage() : im;
+        beginLine = input_stream.getBeginLine();
+        beginColumn = input_stream.getBeginColumn();
+        endLine = input_stream.getEndLine();
+        endColumn = input_stream.getEndColumn();
+        t = Token.newToken(jjmatchedKind);
+        t.kind = jjmatchedKind;
+        t.image = curTokenImage;
+
+        t.beginLine = beginLine;
+        t.endLine = endLine;
+        t.beginColumn = beginColumn;
+        t.endColumn = endColumn;
+
+        return t;
+    }
+
+    int curLexState = 0;
+    int defaultLexState = 0;
+    int jjnewStateCnt;
+    int jjround;
+    int jjmatchedPos;
+    int jjmatchedKind;
+
+    /** Get the next Token. */
+    public Token getNextToken() {
+        Token matchedToken;
+        int curPos = 0;
+
+        EOFLoop: for (;;) {
+            try {
+                curChar = input_stream.BeginToken();
+            } catch (java.io.IOException e) {
+                jjmatchedKind = 0;
+                matchedToken = jjFillToken();
+                return matchedToken;
+            }
+            image = jjimage;
+            image.setLength(0);
+            jjimageLen = 0;
+
+            switch (curLexState) {
+            case 0:
+                jjmatchedKind = 0x7fffffff;
+                jjmatchedPos = 0;
+                curPos = jjMoveStringLiteralDfa0_0();
+                break;
+            case 1:
+                try {
+                    input_stream.backup(0);
+                    while (curChar <= 32 && (0x100002600L & (1L << curChar)) != 0L) {
+                        curChar = input_stream.BeginToken();
+                    }
+                } catch (java.io.IOException e1) {
+                    continue EOFLoop;
+                }
+                jjmatchedKind = 0x7fffffff;
+                jjmatchedPos = 0;
+                curPos = jjMoveStringLiteralDfa0_1();
+                if (jjmatchedPos == 0 && jjmatchedKind > 62) {
+                    jjmatchedKind = 62;
+                }
+                break;
+            case 2:
+                try {
+                    input_stream.backup(0);
+                    while (curChar <= 32 && (0x100002600L & (1L << curChar)) != 0L) {
+                        curChar = input_stream.BeginToken();
+                    }
+                } catch (java.io.IOException e1) {
+                    continue EOFLoop;
+                }
+                jjmatchedKind = 0x7fffffff;
+                jjmatchedPos = 0;
+                curPos = jjMoveStringLiteralDfa0_2();
+                if (jjmatchedPos == 0 && jjmatchedKind > 62) {
+                    jjmatchedKind = 62;
+                }
+                break;
+            }
+            if (jjmatchedKind != 0x7fffffff) {
+                if (jjmatchedPos + 1 < curPos) {
+                    input_stream.backup(curPos - jjmatchedPos - 1);
+                }
+                if ((jjtoToken[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) {
+                    matchedToken = jjFillToken();
+                    TokenLexicalActions(matchedToken);
+                    if (jjnewLexState[jjmatchedKind] != -1) {
+                        curLexState = jjnewLexState[jjmatchedKind];
+                    }
+                    return matchedToken;
+                } else {
+                    if (jjnewLexState[jjmatchedKind] != -1) {
+                        curLexState = jjnewLexState[jjmatchedKind];
+                    }
+                    continue EOFLoop;
+                }
+            }
+            int error_line = input_stream.getEndLine();
+            int error_column = input_stream.getEndColumn();
+            String error_after = null;
+            boolean EOFSeen = false;
+            try {
+                input_stream.readChar();
+                input_stream.backup(1);
+            } catch (java.io.IOException e1) {
+                EOFSeen = true;
+                error_after = curPos <= 1 ? "" : input_stream.GetImage();
+                if (curChar == '\n' || curChar == '\r') {
+                    error_line++;
+                    error_column = 0;
+                } else {
+                    error_column++;
+                }
+            }
+            if (!EOFSeen) {
+                input_stream.backup(1);
+                error_after = curPos <= 1 ? "" : input_stream.GetImage();
+            }
+            throw new TokenMgrError(EOFSeen, curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LEXICAL_ERROR);
+        }
+    }
+
+    void TokenLexicalActions(Token matchedToken) {
+        switch (jjmatchedKind) {
+        case 2:
+            image.append(jjstrLiteralImages[2]);
+            lengthOfMatch = jjstrLiteralImages[2].length();
+            stack.push(DEFAULT);
+            break;
+        case 3:
+            image.append(jjstrLiteralImages[3]);
+            lengthOfMatch = jjstrLiteralImages[3].length();
+            stack.push(DEFAULT);
+            break;
+        case 9:
+            image.append(jjstrLiteralImages[9]);
+            lengthOfMatch = jjstrLiteralImages[9].length();
+            stack.push(curLexState);
+            break;
+        case 10:
+            image.append(jjstrLiteralImages[10]);
+            lengthOfMatch = jjstrLiteralImages[10].length();
+            SwitchTo(stack.pop());
+            break;
+        default:
+            break;
+        }
+    }
+
+    private void jjCheckNAdd(int state) {
+        if (jjrounds[state] != jjround) {
+            jjstateSet[jjnewStateCnt++] = state;
+            jjrounds[state] = jjround;
+        }
+    }
+
+    private void jjAddStates(int start, int end) {
+        do {
+            jjstateSet[jjnewStateCnt++] = jjnextStates[start];
+        } while (start++ != end);
+    }
+
+    private void jjCheckNAddTwoStates(int state1, int state2) {
+        jjCheckNAdd(state1);
+        jjCheckNAdd(state2);
+    }
+
+    private void jjCheckNAddStates(int start, int end) {
+        do {
+            jjCheckNAdd(jjnextStates[start]);
+        } while (start++ != end);
+    }
+
+}
diff --git a/impl/src/main/java/com/sun/el/parser/ELParserTreeConstants.java b/impl/src/main/java/com/sun/el/parser/ELParserTreeConstants.java
new file mode 100644
index 0000000..8a2762f
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/ELParserTreeConstants.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/* Generated By:JavaCC: Do not edit this line. ELParserTreeConstants.java Version 5.0 */
+package com.sun.el.parser;
+
+public interface ELParserTreeConstants {
+    int JJTCOMPOSITEEXPRESSION = 0;
+    int JJTLITERALEXPRESSION = 1;
+    int JJTDEFERREDEXPRESSION = 2;
+    int JJTDYNAMICEXPRESSION = 3;
+    int JJTVOID = 4;
+    int JJTSEMICOLON = 5;
+    int JJTASSIGN = 6;
+    int JJTLAMBDAEXPRESSION = 7;
+    int JJTLAMBDAPARAMETERS = 8;
+    int JJTCHOICE = 9;
+    int JJTOR = 10;
+    int JJTAND = 11;
+    int JJTEQUAL = 12;
+    int JJTNOTEQUAL = 13;
+    int JJTLESSTHAN = 14;
+    int JJTGREATERTHAN = 15;
+    int JJTLESSTHANEQUAL = 16;
+    int JJTGREATERTHANEQUAL = 17;
+    int JJTCONCAT = 18;
+    int JJTPLUS = 19;
+    int JJTMINUS = 20;
+    int JJTMULT = 21;
+    int JJTDIV = 22;
+    int JJTMOD = 23;
+    int JJTNEGATIVE = 24;
+    int JJTNOT = 25;
+    int JJTEMPTY = 26;
+    int JJTVALUE = 27;
+    int JJTDOTSUFFIX = 28;
+    int JJTBRACKETSUFFIX = 29;
+    int JJTMETHODARGUMENTS = 30;
+    int JJTMAPDATA = 31;
+    int JJTMAPENTRY = 32;
+    int JJTLISTDATA = 33;
+    int JJTIDENTIFIER = 34;
+    int JJTFUNCTION = 35;
+    int JJTTRUE = 36;
+    int JJTFALSE = 37;
+    int JJTFLOATINGPOINT = 38;
+    int JJTINTEGER = 39;
+    int JJTSTRING = 40;
+    int JJTNULL = 41;
+
+    String[] jjtNodeName = { "CompositeExpression", "LiteralExpression", "DeferredExpression", "DynamicExpression", "void", "SemiColon", "Assign",
+            "LambdaExpression", "LambdaParameters", "Choice", "Or", "And", "Equal", "NotEqual", "LessThan", "GreaterThan", "LessThanEqual", "GreaterThanEqual",
+            "Concat", "Plus", "Minus", "Mult", "Div", "Mod", "Negative", "Not", "Empty", "Value", "DotSuffix", "BracketSuffix", "MethodArguments", "MapData",
+            "MapEntry", "ListData", "Identifier", "Function", "True", "False", "FloatingPoint", "Integer", "String", "Null", };
+}
+/* JavaCC - OriginalChecksum=295bae338407e43a1d349f1ce802614a (do not edit this line) */
diff --git a/impl/src/main/java/com/sun/el/parser/JJTELParserState.java b/impl/src/main/java/com/sun/el/parser/JJTELParserState.java
new file mode 100644
index 0000000..80c0298
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/JJTELParserState.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/* Generated By:JavaCC: Do not edit this line. JJTELParserState.java Version 5.0 */
+package com.sun.el.parser;
+
+public class JJTELParserState {
+    private java.util.List<Node> nodes;
+    private java.util.List<Integer> marks;
+
+    private int sp; // number of nodes on stack
+    private int mk; // current mark
+    private boolean node_created;
+
+    public JJTELParserState() {
+        nodes = new java.util.ArrayList<Node>();
+        marks = new java.util.ArrayList<Integer>();
+        sp = 0;
+        mk = 0;
+    }
+
+    /*
+     * Determines whether the current node was actually closed and pushed. This should only be called in the final user
+     * action of a node scope.
+     */
+    public boolean nodeCreated() {
+        return node_created;
+    }
+
+    /*
+     * Call this to reinitialize the node stack. It is called automatically by the parser's ReInit() method.
+     */
+    public void reset() {
+        nodes.clear();
+        marks.clear();
+        sp = 0;
+        mk = 0;
+    }
+
+    /*
+     * Returns the root node of the AST. It only makes sense to call this after a successful parse.
+     */
+    public Node rootNode() {
+        return nodes.get(0);
+    }
+
+    /* Pushes a node on to the stack. */
+    public void pushNode(Node n) {
+        nodes.add(n);
+        ++sp;
+    }
+
+    /*
+     * Returns the node on the top of the stack, and remove it from the stack.
+     */
+    public Node popNode() {
+        if (--sp < mk) {
+            mk = marks.remove(marks.size() - 1);
+        }
+        return nodes.remove(nodes.size() - 1);
+    }
+
+    /* Returns the node currently on the top of the stack. */
+    public Node peekNode() {
+        return nodes.get(nodes.size() - 1);
+    }
+
+    /*
+     * Returns the number of children on the stack in the current node scope.
+     */
+    public int nodeArity() {
+        return sp - mk;
+    }
+
+    public void clearNodeScope(Node n) {
+        while (sp > mk) {
+            popNode();
+        }
+        mk = marks.remove(marks.size() - 1);
+    }
+
+    public void openNodeScope(Node n) {
+        marks.add(mk);
+        mk = sp;
+        n.jjtOpen();
+    }
+
+    /*
+     * A definite node is constructed from a specified number of children. That number of nodes are popped from the stack
+     * and made the children of the definite node. Then the definite node is pushed on to the stack.
+     */
+    public void closeNodeScope(Node n, int num) {
+        mk = marks.remove(marks.size() - 1);
+        while (num-- > 0) {
+            Node c = popNode();
+            c.jjtSetParent(n);
+            n.jjtAddChild(c, num);
+        }
+        n.jjtClose();
+        pushNode(n);
+        node_created = true;
+    }
+
+    /*
+     * A conditional node is constructed if its condition is true. All the nodes that have been pushed since the node was
+     * opened are made children of the conditional node, which is then pushed on to the stack. If the condition is false the
+     * node is not constructed and they are left on the stack.
+     */
+    public void closeNodeScope(Node n, boolean condition) {
+        if (condition) {
+            int a = nodeArity();
+            mk = marks.remove(marks.size() - 1);
+            while (a-- > 0) {
+                Node c = popNode();
+                c.jjtSetParent(n);
+                n.jjtAddChild(c, a);
+            }
+            n.jjtClose();
+            pushNode(n);
+            node_created = true;
+        } else {
+            mk = marks.remove(marks.size() - 1);
+            node_created = false;
+        }
+    }
+}
+/* JavaCC - OriginalChecksum=a169ec9bf66edaa6db0c5550b112beee (do not edit this line) */
diff --git a/impl/src/main/java/com/sun/el/parser/Node.java b/impl/src/main/java/com/sun/el/parser/Node.java
new file mode 100644
index 0000000..be71187
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/Node.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+import javax.el.MethodInfo;
+import javax.el.ValueReference;
+
+import com.sun.el.lang.EvaluationContext;
+
+/* All AST nodes must implement this interface.  It provides basic
+   machinery for constructing the parent and child relationships
+   between nodes. */
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public interface Node {
+
+    /**
+     * This method is called after the node has been made the current node. It indicates that child nodes can now be added
+     * to it.
+     */
+    void jjtOpen();
+
+    /**
+     * This method is called after all the child nodes have been added.
+     */
+    void jjtClose();
+
+    /**
+     * This pair of methods are used to inform the node of its parent.
+     */
+    void jjtSetParent(Node n);
+
+    Node jjtGetParent();
+
+    /**
+     * This method tells the node to add its argument to the node's list of children.
+     */
+    void jjtAddChild(Node n, int i);
+
+    /**
+     * This method returns a child node. The children are numbered from zero, left to right.
+     */
+    Node jjtGetChild(int i);
+
+    /** Return the number of children the node has. */
+    int jjtGetNumChildren();
+
+    String getImage();
+
+    Object getValue(EvaluationContext ctx) throws ELException;
+
+    void setValue(EvaluationContext ctx, Object value) throws ELException;
+
+    Class getType(EvaluationContext ctx) throws ELException;
+
+    ValueReference getValueReference(EvaluationContext ctx) throws ELException;
+
+    boolean isReadOnly(EvaluationContext ctx) throws ELException;
+
+    void accept(NodeVisitor visitor) throws ELException;
+
+    MethodInfo getMethodInfo(EvaluationContext ctx, Class[] paramTypes) throws ELException;
+
+    Object invoke(EvaluationContext ctx, Class[] paramTypes, Object[] paramValues) throws ELException;
+
+    @Override
+    boolean equals(Object n);
+
+    @Override
+    int hashCode();
+
+    boolean isParametersProvided();
+}
diff --git a/impl/src/main/java/com/sun/el/parser/NodeVisitor.java b/impl/src/main/java/com/sun/el/parser/NodeVisitor.java
new file mode 100644
index 0000000..e18922c
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/NodeVisitor.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public interface NodeVisitor {
+    void visit(Node node) throws ELException;
+}
diff --git a/impl/src/main/java/com/sun/el/parser/ParseException.java b/impl/src/main/java/com/sun/el/parser/ParseException.java
new file mode 100644
index 0000000..ac9dee3
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/ParseException.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+/**
+ * This exception is thrown when parse errors are encountered. You can explicitly create objects of this exception type
+ * by calling the method generateParseException in the generated parser.
+ *
+ * You can modify this class to customize your error reporting mechanisms so long as you retain the public fields.
+ */
+public class ParseException extends Exception {
+
+    /**
+     * This constructor is used by the method "generateParseException" in the generated parser. Calling this constructor
+     * generates a new object of this type with the fields "currentToken", "expectedTokenSequences", and "tokenImage" set.
+     * The boolean flag "specialConstructor" is also set to true to indicate that this constructor was used to create this
+     * object. This constructor calls its super class with the empty string to force the "toString" method of parent class
+     * "Throwable" to print the error message in the form: ParseException: <result of getMessage>
+     */
+    public ParseException(Token currentTokenVal, int[][] expectedTokenSequencesVal, String[] tokenImageVal) {
+        super("");
+        specialConstructor = true;
+        currentToken = currentTokenVal;
+        expectedTokenSequences = expectedTokenSequencesVal;
+        tokenImage = tokenImageVal;
+    }
+
+    /**
+     * The following constructors are for use by you for whatever purpose you can think of. Constructing the exception in
+     * this manner makes the exception behave in the normal way - i.e., as documented in the class "Throwable". The fields
+     * "errorToken", "expectedTokenSequences", and "tokenImage" do not contain relevant information. The JavaCC generated
+     * code does not use these constructors.
+     */
+
+    public ParseException() {
+        super();
+        specialConstructor = false;
+    }
+
+    public ParseException(String message) {
+        super(message);
+        specialConstructor = false;
+    }
+
+    /**
+     * This variable determines which constructor was used to create this object and thereby affects the semantics of the
+     * "getMessage" method (see below).
+     */
+    protected boolean specialConstructor;
+
+    /**
+     * This is the last token that has been consumed successfully. If this object has been created due to a parse error, the
+     * token followng this token will (therefore) be the first error token.
+     */
+    public Token currentToken;
+
+    /**
+     * Each entry in this array is an array of integers. Each array of integers represents a sequence of tokens (by their
+     * ordinal values) that is expected at this point of the parse.
+     */
+    public int[][] expectedTokenSequences;
+
+    /**
+     * This is a reference to the "tokenImage" array of the generated parser within which the parse error occurred. This
+     * array is defined in the generated ...Constants interface.
+     */
+    public String[] tokenImage;
+
+    /**
+     * This method has the standard behavior when this object has been created using the standard constructors. Otherwise,
+     * it uses "currentToken" and "expectedTokenSequences" to generate a parse error message and returns it. If this object
+     * has been created due to a parse error, and you do not catch it (it gets thrown from the parser), then this method is
+     * called during the printing of the final stack trace, and hence the correct error message gets displayed.
+     */
+    @Override
+    public String getMessage() {
+        if (!specialConstructor) {
+            return super.getMessage();
+        }
+        String expected = "";
+        int maxSize = 0;
+        for (int i = 0; i < expectedTokenSequences.length; i++) {
+            if (maxSize < expectedTokenSequences[i].length) {
+                maxSize = expectedTokenSequences[i].length;
+            }
+            for (int j = 0; j < expectedTokenSequences[i].length; j++) {
+                expected += tokenImage[expectedTokenSequences[i][j]] + " ";
+            }
+            if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) {
+                expected += "...";
+            }
+            expected += eol + "    ";
+        }
+        String retval = "Encountered \"";
+        Token tok = currentToken.next;
+        for (int i = 0; i < maxSize; i++) {
+            if (i != 0) {
+                retval += " ";
+            }
+            if (tok.kind == 0) {
+                retval += tokenImage[0];
+                break;
+            }
+            retval += add_escapes(tok.image);
+            tok = tok.next;
+        }
+        retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn;
+        retval += "." + eol;
+        if (expectedTokenSequences.length == 1) {
+            retval += "Was expecting:" + eol + "    ";
+        } else {
+            retval += "Was expecting one of:" + eol + "    ";
+        }
+        retval += expected;
+        return retval;
+    }
+
+    /**
+     * The end of line string for this machine.
+     */
+    protected String eol = System.getProperty("line.separator", "\n");
+
+    /**
+     * Used to convert raw characters to their escaped version when these raw version cannot be used as part of an ASCII
+     * string literal.
+     */
+    protected String add_escapes(String str) {
+        StringBuffer retval = new StringBuffer();
+        char ch;
+        for (int i = 0; i < str.length(); i++) {
+            switch (str.charAt(i)) {
+            case 0:
+                continue;
+            case '\b':
+                retval.append("\\b");
+                continue;
+            case '\t':
+                retval.append("\\t");
+                continue;
+            case '\n':
+                retval.append("\\n");
+                continue;
+            case '\f':
+                retval.append("\\f");
+                continue;
+            case '\r':
+                retval.append("\\r");
+                continue;
+            case '\"':
+                retval.append("\\\"");
+                continue;
+            case '\'':
+                retval.append("\\\'");
+                continue;
+            case '\\':
+                retval.append("\\\\");
+                continue;
+            default:
+                if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+                    String s = "0000" + Integer.toString(ch, 16);
+                    retval.append("\\u" + s.substring(s.length() - 4, s.length()));
+                } else {
+                    retval.append(ch);
+                }
+                continue;
+            }
+        }
+        return retval.toString();
+    }
+
+}
diff --git a/impl/src/main/java/com/sun/el/parser/SimpleCharStream.java b/impl/src/main/java/com/sun/el/parser/SimpleCharStream.java
new file mode 100644
index 0000000..4b0b619
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/SimpleCharStream.java
@@ -0,0 +1,436 @@
+/*
+ * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/* Generated By:JavaCC: Do not edit this line. SimpleCharStream.java Version 5.0 */
+/* JavaCCOptions:STATIC=false,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
+package com.sun.el.parser;
+
+/**
+ * An implementation of interface CharStream, where the stream is assumed to contain only ASCII characters (without
+ * unicode processing).
+ */
+
+public class SimpleCharStream {
+    /** Whether parser is static. */
+    public static final boolean staticFlag = false;
+    int bufsize;
+    int available;
+    int tokenBegin;
+    /** Position in buffer. */
+    public int bufpos = -1;
+    protected int bufline[];
+    protected int bufcolumn[];
+
+    protected int column = 0;
+    protected int line = 1;
+
+    protected boolean prevCharIsCR = false;
+    protected boolean prevCharIsLF = false;
+
+    protected java.io.Reader inputStream;
+
+    protected char[] buffer;
+    protected int maxNextCharInd = 0;
+    protected int inBuf = 0;
+    protected int tabSize = 8;
+
+    protected void setTabSize(int i) {
+        tabSize = i;
+    }
+
+    protected int getTabSize(int i) {
+        return tabSize;
+    }
+
+    protected void ExpandBuff(boolean wrapAround) {
+        char[] newbuffer = new char[bufsize + 2048];
+        int newbufline[] = new int[bufsize + 2048];
+        int newbufcolumn[] = new int[bufsize + 2048];
+
+        try {
+            if (wrapAround) {
+                System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+                System.arraycopy(buffer, 0, newbuffer, bufsize - tokenBegin, bufpos);
+                buffer = newbuffer;
+
+                System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+                System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos);
+                bufline = newbufline;
+
+                System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+                System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos);
+                bufcolumn = newbufcolumn;
+
+                maxNextCharInd = (bufpos += (bufsize - tokenBegin));
+            } else {
+                System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+                buffer = newbuffer;
+
+                System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+                bufline = newbufline;
+
+                System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+                bufcolumn = newbufcolumn;
+
+                maxNextCharInd = (bufpos -= tokenBegin);
+            }
+        } catch (Throwable t) {
+            throw new Error(t.getMessage());
+        }
+
+        bufsize += 2048;
+        available = bufsize;
+        tokenBegin = 0;
+    }
+
+    protected void FillBuff() throws java.io.IOException {
+        if (maxNextCharInd == available) {
+            if (available == bufsize) {
+                if (tokenBegin > 2048) {
+                    bufpos = maxNextCharInd = 0;
+                    available = tokenBegin;
+                } else if (tokenBegin < 0) {
+                    bufpos = maxNextCharInd = 0;
+                } else {
+                    ExpandBuff(false);
+                }
+            } else if (available > tokenBegin) {
+                available = bufsize;
+            } else if ((tokenBegin - available) < 2048) {
+                ExpandBuff(true);
+            } else {
+                available = tokenBegin;
+            }
+        }
+
+        int i;
+        try {
+            if ((i = inputStream.read(buffer, maxNextCharInd, available - maxNextCharInd)) == -1) {
+                inputStream.close();
+                throw new java.io.IOException();
+            } else {
+                maxNextCharInd += i;
+            }
+            return;
+        } catch (java.io.IOException e) {
+            --bufpos;
+            backup(0);
+            if (tokenBegin == -1) {
+                tokenBegin = bufpos;
+            }
+            throw e;
+        }
+    }
+
+    /** Start. */
+    public char BeginToken() throws java.io.IOException {
+        tokenBegin = -1;
+        char c = readChar();
+        tokenBegin = bufpos;
+
+        return c;
+    }
+
+    protected void UpdateLineColumn(char c) {
+        column++;
+
+        if (prevCharIsLF) {
+            prevCharIsLF = false;
+            line += (column = 1);
+        } else if (prevCharIsCR) {
+            prevCharIsCR = false;
+            if (c == '\n') {
+                prevCharIsLF = true;
+            } else {
+                line += (column = 1);
+            }
+        }
+
+        switch (c) {
+        case '\r':
+            prevCharIsCR = true;
+            break;
+        case '\n':
+            prevCharIsLF = true;
+            break;
+        case '\t':
+            column--;
+            column += (tabSize - (column % tabSize));
+            break;
+        default:
+            break;
+        }
+
+        bufline[bufpos] = line;
+        bufcolumn[bufpos] = column;
+    }
+
+    /** Read a character. */
+    public char readChar() throws java.io.IOException {
+        if (inBuf > 0) {
+            --inBuf;
+
+            if (++bufpos == bufsize) {
+                bufpos = 0;
+            }
+
+            return buffer[bufpos];
+        }
+
+        if (++bufpos >= maxNextCharInd) {
+            FillBuff();
+        }
+
+        char c = buffer[bufpos];
+
+        UpdateLineColumn(c);
+        return c;
+    }
+
+    @Deprecated
+    /**
+     * @deprecated
+     * @see #getEndColumn
+     */
+
+    public int getColumn() {
+        return bufcolumn[bufpos];
+    }
+
+    @Deprecated
+    /**
+     * @deprecated
+     * @see #getEndLine
+     */
+
+    public int getLine() {
+        return bufline[bufpos];
+    }
+
+    /** Get token end column number. */
+    public int getEndColumn() {
+        return bufcolumn[bufpos];
+    }
+
+    /** Get token end line number. */
+    public int getEndLine() {
+        return bufline[bufpos];
+    }
+
+    /** Get token beginning column number. */
+    public int getBeginColumn() {
+        return bufcolumn[tokenBegin];
+    }
+
+    /** Get token beginning line number. */
+    public int getBeginLine() {
+        return bufline[tokenBegin];
+    }
+
+    /** Backup a number of characters. */
+    public void backup(int amount) {
+
+        inBuf += amount;
+        if ((bufpos -= amount) < 0) {
+            bufpos += bufsize;
+        }
+    }
+
+    /** Constructor. */
+    public SimpleCharStream(java.io.Reader dstream, int startline, int startcolumn, int buffersize) {
+        inputStream = dstream;
+        line = startline;
+        column = startcolumn - 1;
+
+        available = bufsize = buffersize;
+        buffer = new char[buffersize];
+        bufline = new int[buffersize];
+        bufcolumn = new int[buffersize];
+    }
+
+    /** Constructor. */
+    public SimpleCharStream(java.io.Reader dstream, int startline, int startcolumn) {
+        this(dstream, startline, startcolumn, 4096);
+    }
+
+    /** Constructor. */
+    public SimpleCharStream(java.io.Reader dstream) {
+        this(dstream, 1, 1, 4096);
+    }
+
+    /** Reinitialise. */
+    public void ReInit(java.io.Reader dstream, int startline, int startcolumn, int buffersize) {
+        inputStream = dstream;
+        line = startline;
+        column = startcolumn - 1;
+
+        if (buffer == null || buffersize != buffer.length) {
+            available = bufsize = buffersize;
+            buffer = new char[buffersize];
+            bufline = new int[buffersize];
+            bufcolumn = new int[buffersize];
+        }
+        prevCharIsLF = prevCharIsCR = false;
+        tokenBegin = inBuf = maxNextCharInd = 0;
+        bufpos = -1;
+    }
+
+    /** Reinitialise. */
+    public void ReInit(java.io.Reader dstream, int startline, int startcolumn) {
+        ReInit(dstream, startline, startcolumn, 4096);
+    }
+
+    /** Reinitialise. */
+    public void ReInit(java.io.Reader dstream) {
+        ReInit(dstream, 1, 1, 4096);
+    }
+
+    /** Constructor. */
+    public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline, int startcolumn, int buffersize)
+            throws java.io.UnsupportedEncodingException {
+        this(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize);
+    }
+
+    /** Constructor. */
+    public SimpleCharStream(java.io.InputStream dstream, int startline, int startcolumn, int buffersize) {
+        this(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
+    }
+
+    /** Constructor. */
+    public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline, int startcolumn) throws java.io.UnsupportedEncodingException {
+        this(dstream, encoding, startline, startcolumn, 4096);
+    }
+
+    /** Constructor. */
+    public SimpleCharStream(java.io.InputStream dstream, int startline, int startcolumn) {
+        this(dstream, startline, startcolumn, 4096);
+    }
+
+    /** Constructor. */
+    public SimpleCharStream(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException {
+        this(dstream, encoding, 1, 1, 4096);
+    }
+
+    /** Constructor. */
+    public SimpleCharStream(java.io.InputStream dstream) {
+        this(dstream, 1, 1, 4096);
+    }
+
+    /** Reinitialise. */
+    public void ReInit(java.io.InputStream dstream, String encoding, int startline, int startcolumn, int buffersize)
+            throws java.io.UnsupportedEncodingException {
+        ReInit(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn,
+                buffersize);
+    }
+
+    /** Reinitialise. */
+    public void ReInit(java.io.InputStream dstream, int startline, int startcolumn, int buffersize) {
+        ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
+    }
+
+    /** Reinitialise. */
+    public void ReInit(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException {
+        ReInit(dstream, encoding, 1, 1, 4096);
+    }
+
+    /** Reinitialise. */
+    public void ReInit(java.io.InputStream dstream) {
+        ReInit(dstream, 1, 1, 4096);
+    }
+
+    /** Reinitialise. */
+    public void ReInit(java.io.InputStream dstream, String encoding, int startline, int startcolumn) throws java.io.UnsupportedEncodingException {
+        ReInit(dstream, encoding, startline, startcolumn, 4096);
+    }
+
+    /** Reinitialise. */
+    public void ReInit(java.io.InputStream dstream, int startline, int startcolumn) {
+        ReInit(dstream, startline, startcolumn, 4096);
+    }
+
+    /** Get token literal value. */
+    public String GetImage() {
+        if (bufpos >= tokenBegin) {
+            return new String(buffer, tokenBegin, bufpos - tokenBegin + 1);
+        } else {
+            return new String(buffer, tokenBegin, bufsize - tokenBegin) + new String(buffer, 0, bufpos + 1);
+        }
+    }
+
+    /** Get the suffix. */
+    public char[] GetSuffix(int len) {
+        char[] ret = new char[len];
+
+        if ((bufpos + 1) >= len) {
+            System.arraycopy(buffer, bufpos - len + 1, ret, 0, len);
+        } else {
+            System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0, len - bufpos - 1);
+            System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1);
+        }
+
+        return ret;
+    }
+
+    /** Reset buffer when finished. */
+    public void Done() {
+        buffer = null;
+        bufline = null;
+        bufcolumn = null;
+    }
+
+    /**
+     * Method to adjust line and column numbers for the start of a token.
+     */
+    public void adjustBeginLineColumn(int newLine, int newCol) {
+        int start = tokenBegin;
+        int len;
+
+        if (bufpos >= tokenBegin) {
+            len = bufpos - tokenBegin + inBuf + 1;
+        } else {
+            len = bufsize - tokenBegin + bufpos + 1 + inBuf;
+        }
+
+        int i = 0, j = 0, k = 0;
+        int nextColDiff = 0, columnDiff = 0;
+
+        while (i < len && bufline[j = start % bufsize] == bufline[k = ++start % bufsize]) {
+            bufline[j] = newLine;
+            nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j];
+            bufcolumn[j] = newCol + columnDiff;
+            columnDiff = nextColDiff;
+            i++;
+        }
+
+        if (i < len) {
+            bufline[j] = newLine++;
+            bufcolumn[j] = newCol + columnDiff;
+
+            while (i++ < len) {
+                if (bufline[j = start % bufsize] != bufline[++start % bufsize]) {
+                    bufline[j] = newLine++;
+                } else {
+                    bufline[j] = newLine;
+                }
+            }
+        }
+
+        line = bufline[j];
+        column = bufcolumn[j];
+    }
+
+}
+/* JavaCC - OriginalChecksum=7ea14199259e7ce0336b228c8cdb9958 (do not edit this line) */
diff --git a/impl/src/main/java/com/sun/el/parser/SimpleNode.java b/impl/src/main/java/com/sun/el/parser/SimpleNode.java
new file mode 100644
index 0000000..1c004c6
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/SimpleNode.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import javax.el.ELException;
+import javax.el.MethodInfo;
+import javax.el.PropertyNotWritableException;
+import javax.el.ValueReference;
+
+import com.sun.el.lang.ELSupport;
+import com.sun.el.lang.EvaluationContext;
+import com.sun.el.util.MessageFactory;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public abstract class SimpleNode extends ELSupport implements Node {
+    protected Node parent;
+
+    protected Node[] children;
+
+    protected int id;
+
+    protected String image;
+
+    public SimpleNode(int i) {
+        id = i;
+    }
+
+    @Override
+    public void jjtOpen() {
+    }
+
+    @Override
+    public void jjtClose() {
+    }
+
+    @Override
+    public void jjtSetParent(Node n) {
+        parent = n;
+    }
+
+    @Override
+    public Node jjtGetParent() {
+        return parent;
+    }
+
+    @Override
+    public void jjtAddChild(Node n, int i) {
+        if (children == null) {
+            children = new Node[i + 1];
+        } else if (i >= children.length) {
+            Node c[] = new Node[i + 1];
+            System.arraycopy(children, 0, c, 0, children.length);
+            children = c;
+        }
+        children[i] = n;
+    }
+
+    @Override
+    public Node jjtGetChild(int i) {
+        return children[i];
+    }
+
+    @Override
+    public int jjtGetNumChildren() {
+        return (children == null) ? 0 : children.length;
+    }
+
+    /*
+     * You can override these two methods in subclasses of SimpleNode to customize the way the node 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() {
+        if (this.image != null) {
+            return ELParserTreeConstants.jjtNodeName[id] + "[" + this.image + "]";
+        }
+        return ELParserTreeConstants.jjtNodeName[id];
+    }
+
+    public String toString(String prefix) {
+        return prefix + toString();
+    }
+
+    /*
+     * Override this method if you want to customize how the node 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 = (SimpleNode) children[i];
+                if (n != null) {
+                    n.dump(prefix + " ");
+                }
+            }
+        }
+    }
+
+    @Override
+    public String getImage() {
+        return image;
+    }
+
+    public void setImage(String image) {
+        this.image = image;
+    }
+
+    @Override
+    public Class getType(EvaluationContext ctx) throws ELException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public ValueReference getValueReference(EvaluationContext ctx) throws ELException {
+        return null;
+    }
+
+    @Override
+    public boolean isReadOnly(EvaluationContext ctx) throws ELException {
+        return true;
+    }
+
+    @Override
+    public void setValue(EvaluationContext ctx, Object value) throws ELException {
+        throw new PropertyNotWritableException(MessageFactory.get("error.syntax.set"));
+    }
+
+    @Override
+    public void accept(NodeVisitor visitor) throws ELException {
+        visitor.visit(this);
+        if (this.children != null && this.children.length > 0) {
+            for (int i = 0; i < this.children.length; i++) {
+                this.children[i].accept(visitor);
+            }
+        }
+    }
+
+    @Override
+    public Object invoke(EvaluationContext ctx, Class[] paramTypes, Object[] paramValues) throws ELException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public MethodInfo getMethodInfo(EvaluationContext ctx, Class[] paramTypes) throws ELException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean equals(Object node) {
+        if (!(node instanceof SimpleNode)) {
+            return false;
+        }
+        SimpleNode n = (SimpleNode) node;
+        if (this.id != n.id) {
+            return false;
+        }
+        if (this.children == null && n.children == null) {
+            if (this.image == null) {
+                return n.image == null;
+            }
+            return this.image.equals(n.image);
+        }
+        if (this.children == null || n.children == null) {
+            // One is null and the other is non-null
+            return false;
+        }
+        if (this.children.length != n.children.length) {
+            return false;
+        }
+        if (this.children.length == 0) {
+            if (this.image == null) {
+                return n.image == null;
+            }
+            return this.image.equals(n.image);
+        }
+        for (int i = 0; i < this.children.length; i++) {
+            if (!this.children[i].equals(n.children[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean isParametersProvided() {
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        if (this.children == null || this.children.length == 0) {
+            if (this.image != null) {
+                return this.image.hashCode();
+            }
+            return this.id;
+        }
+        int h = 0;
+        for (int i = this.children.length - 1; i >= 0; i--) {
+            h = h + h + h + this.children[i].hashCode();
+        }
+        h = h + h + h + id;
+        return h;
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/parser/Token.java b/impl/src/main/java/com/sun/el/parser/Token.java
new file mode 100644
index 0000000..54eab3e
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/Token.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+import java.io.Serializable;
+
+/**
+ * Describes the input token stream.
+ */
+
+public class Token implements Serializable {
+
+    /**
+     * An integer that describes the kind of this token. This numbering system is determined by JavaCCParser, and a table of
+     * these numbers is stored in the file ...Constants.java.
+     */
+    public int kind;
+
+    /**
+     * beginLine and beginColumn describe the position of the first character of this token; endLine and endColumn describe
+     * the position of the last character of this token.
+     */
+    public int beginLine, beginColumn, endLine, endColumn;
+
+    /**
+     * The string image of the token.
+     */
+    public String image;
+
+    /**
+     * A reference to the next regular (non-special) token from the input stream. If this is the last token from the input
+     * stream, or if the token manager has not read tokens beyond this one, this field is set to null. This is true only if
+     * this token is also a regular token. Otherwise, see below for a description of the contents of this field.
+     */
+    public Token next;
+
+    /**
+     * This field is used to access special tokens that occur prior to this token, but after the immediately preceding
+     * regular (non-special) token. If there are no such special tokens, this field is set to null. When there are more than
+     * one such special token, this field refers to the last of these special tokens, which in turn refers to the next
+     * previous special token through its specialToken field, and so on until the first special token (whose specialToken
+     * field is null). The next fields of special tokens refer to other special tokens that immediately follow it (without
+     * an intervening regular token). If there is no such token, this field is null.
+     */
+    public Token specialToken;
+
+    /**
+     * Returns the image.
+     */
+    @Override
+    public String toString() {
+        return image;
+    }
+
+    /**
+     * Returns a new Token object, by default. However, if you want, you can create and return subclass objects based on the
+     * value of ofKind. Simply add the cases to the switch for all those special cases. For example, if you have a subclass
+     * of Token called IDToken that you want to create if ofKind is ID, simlpy add something like :
+     *
+     * case MyParserConstants.ID : return new IDToken();
+     *
+     * to the following switch statement. Then you can cast matchedToken variable to the appropriate type and use it in your
+     * lexical actions.
+     */
+    public static final Token newToken(int ofKind) {
+        switch (ofKind) {
+        default:
+            return new Token();
+        }
+    }
+
+}
diff --git a/impl/src/main/java/com/sun/el/parser/TokenMgrError.java b/impl/src/main/java/com/sun/el/parser/TokenMgrError.java
new file mode 100644
index 0000000..ae39207
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/TokenMgrError.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.parser;
+
+public class TokenMgrError extends Error {
+    /*
+     * Ordinals for various reasons why an Error of this type can be thrown.
+     */
+
+    /**
+     * Lexical error occured.
+     */
+    static final int LEXICAL_ERROR = 0;
+
+    /**
+     * An attempt wass made to create a second instance of a static token manager.
+     */
+    static final int STATIC_LEXER_ERROR = 1;
+
+    /**
+     * Tried to change to an invalid lexical state.
+     */
+    static final int INVALID_LEXICAL_STATE = 2;
+
+    /**
+     * Detected (and bailed out of) an infinite loop in the token manager.
+     */
+    static final int LOOP_DETECTED = 3;
+
+    /**
+     * Indicates the reason why the exception is thrown. It will have one of the above 4 values.
+     */
+    int errorCode;
+
+    /**
+     * Replaces unprintable characters by their espaced (or unicode escaped) equivalents in the given string
+     */
+    protected static final String addEscapes(String str) {
+        StringBuffer retval = new StringBuffer();
+        char ch;
+        for (int i = 0; i < str.length(); i++) {
+            switch (str.charAt(i)) {
+            case 0:
+                continue;
+            case '\b':
+                retval.append("\\b");
+                continue;
+            case '\t':
+                retval.append("\\t");
+                continue;
+            case '\n':
+                retval.append("\\n");
+                continue;
+            case '\f':
+                retval.append("\\f");
+                continue;
+            case '\r':
+                retval.append("\\r");
+                continue;
+            case '\"':
+                retval.append("\\\"");
+                continue;
+            case '\'':
+                retval.append("\\\'");
+                continue;
+            case '\\':
+                retval.append("\\\\");
+                continue;
+            default:
+                if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+                    String s = "0000" + Integer.toString(ch, 16);
+                    retval.append("\\u" + s.substring(s.length() - 4, s.length()));
+                } else {
+                    retval.append(ch);
+                }
+                continue;
+            }
+        }
+        return retval.toString();
+    }
+
+    /**
+     * Returns a detailed message for the Error when it is thrown by the token manager to indicate a lexical error.
+     * Parameters : EOFSeen : indicates if EOF caused the lexicl error curLexState : lexical state in which this error
+     * occured errorLine : line number when the error occured errorColumn : column number when the error occured errorAfter
+     * : prefix that was seen before this error occured curchar : the offending character Note: You can customize the
+     * lexical error message by modifying this method.
+     */
+    protected static String LexicalError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar) {
+        return ("Lexical error at line " + errorLine + ", column " + errorColumn + ".  Encountered: "
+                + (EOFSeen ? "<EOF> " : ("\"" + addEscapes(String.valueOf(curChar)) + "\"") + " (" + (int) curChar + "), ") + "after : \""
+                + addEscapes(errorAfter) + "\"");
+    }
+
+    /**
+     * You can also modify the body of this method to customize your error messages. For example, cases like LOOP_DETECTED
+     * and INVALID_LEXICAL_STATE are not of end-users concern, so you can return something like :
+     *
+     * "Internal Error : Please file a bug report .... "
+     *
+     * from this method for such cases in the release version of your parser.
+     */
+    @Override
+    public String getMessage() {
+        return super.getMessage();
+    }
+
+    /*
+     * Constructors of various flavors follow.
+     */
+
+    public TokenMgrError() {
+    }
+
+    public TokenMgrError(String message, int reason) {
+        super(message);
+        errorCode = reason;
+    }
+
+    public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar, int reason) {
+        this(LexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason);
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/stream/Operator.java b/impl/src/main/java/com/sun/el/stream/Operator.java
new file mode 100644
index 0000000..4793ce3
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/stream/Operator.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.stream;
+
+import java.util.Iterator;
+
+interface Operator {
+
+    Iterator<Object> iterator(Iterator<Object> upstream);
+}
diff --git a/impl/src/main/java/com/sun/el/stream/Optional.java b/impl/src/main/java/com/sun/el/stream/Optional.java
new file mode 100644
index 0000000..2ea52b6
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/stream/Optional.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.stream;
+
+import javax.el.LambdaExpression;
+
+public class Optional {
+
+    private final static Optional EMPTY = new Optional();
+    private final Object value;
+
+    Optional(Object value) {
+        if (value == null) {
+            throw new NullPointerException();
+        }
+        this.value = value;
+    }
+
+    Optional() {
+        this.value = null;
+    }
+
+    public boolean isPresent() {
+        return value != null;
+    }
+
+    public void ifPresent(LambdaExpression lambda) {
+        if (value != null) {
+            lambda.invoke(value);
+        }
+    }
+
+    public Object get() {
+        if (value == null) {
+            throw new java.util.NoSuchElementException("No value present");
+        }
+        return value;
+    }
+
+    public Object orElse(Object other) {
+        return value != null ? value : other;
+    }
+
+    public Object orElseGet(LambdaExpression other) {
+        return value != null ? value : other.invoke();
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/stream/Stream.java b/impl/src/main/java/com/sun/el/stream/Stream.java
new file mode 100644
index 0000000..9091271
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/stream/Stream.java
@@ -0,0 +1,526 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.stream;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.PriorityQueue;
+import java.util.Set;
+
+import javax.el.ELException;
+import javax.el.LambdaExpression;
+
+import com.sun.el.lang.ELArithmetic;
+import com.sun.el.lang.ELSupport;
+
+/*
+ */
+
+public class Stream {
+
+    private Iterator<Object> source;
+    private Stream upstream;
+    private Operator op;
+
+    Stream(Iterator<Object> source) {
+        this.source = source;
+    }
+
+    Stream(Stream upstream, Operator op) {
+        this.upstream = upstream;
+        this.op = op;
+    }
+
+    public Iterator<Object> iterator() {
+        if (source != null) {
+            return source;
+        }
+
+        return op.iterator(upstream.iterator());
+    }
+
+    public Stream filter(final LambdaExpression predicate) {
+        return new Stream(this, new Operator() {
+            @Override
+            public Iterator<Object> iterator(final Iterator<Object> upstream) {
+                return new Iterator2(upstream) {
+                    @Override
+                    public void doItem(Object item) {
+                        if ((Boolean) predicate.invoke(item)) {
+                            yield(item);
+                        }
+                    }
+                };
+            }
+        });
+    }
+
+    public Stream map(final LambdaExpression mapper) {
+        return new Stream(this, new Operator() {
+            @Override
+            public Iterator<Object> iterator(final Iterator<Object> up) {
+                return new Iterator1(up) {
+                    @Override
+                    public Object next() {
+                        return mapper.invoke(iter.next());
+                    }
+                };
+            }
+        });
+    }
+
+    public Stream peek(final LambdaExpression comsumer) {
+        return new Stream(this, new Operator() {
+            @Override
+            public Iterator<Object> iterator(final Iterator<Object> up) {
+                return new Iterator2(up) {
+                    @Override
+                    void doItem(Object item) {
+                        comsumer.invoke(item);
+                        yield(item);
+                    }
+                };
+            }
+        });
+    }
+
+    public Stream limit(final long n) {
+        if (n < 0) {
+            throw new IllegalArgumentException("limit must be non-negative");
+        }
+        return new Stream(this, new Operator() {
+            @Override
+            public Iterator<Object> iterator(final Iterator<Object> up) {
+                return new Iterator0() {
+                    long limit = n;
+
+                    @Override
+                    public boolean hasNext() {
+                        return (limit > 0) ? up.hasNext() : false;
+                    }
+
+                    @Override
+                    public Object next() {
+                        limit--;
+                        return up.next();
+                    }
+                };
+            }
+        });
+    }
+
+    public Stream substream(final long startIndex) {
+        if (startIndex < 0) {
+            throw new IllegalArgumentException("substream index must be non-negative");
+        }
+        return new Stream(this, new Operator() {
+            long skip = startIndex;
+
+            @Override
+            public Iterator<Object> iterator(final Iterator<Object> up) {
+                while (skip > 0 && up.hasNext()) {
+                    up.next();
+                    skip--;
+                }
+                return up;
+            }
+        });
+    }
+
+    public Stream substream(long startIndex, long endIndex) {
+        return substream(startIndex).limit(endIndex - startIndex);
+    }
+
+    public Stream distinct() {
+        return new Stream(this, new Operator() {
+            @Override
+            public Iterator<Object> iterator(final Iterator<Object> up) {
+                return new Iterator2(up) {
+                    private Set<Object> set = new HashSet<Object>();
+
+                    @Override
+                    public void doItem(Object item) {
+                        if (set.add(item)) {
+                            yield(item);
+                        }
+                    }
+                };
+            }
+        });
+    }
+
+    public Stream sorted() {
+        return new Stream(this, new Operator() {
+
+            private PriorityQueue<Object> queue = null;
+
+            @Override
+            public Iterator<Object> iterator(final Iterator<Object> up) {
+                if (queue == null) {
+                    queue = new PriorityQueue<Object>(16, new Comparator<Object>() {
+                        @Override
+                        public int compare(Object o1, Object o2) {
+                            return ((Comparable) o1).compareTo(o2);
+                        }
+                    });
+
+                    while (up.hasNext()) {
+                        queue.add(up.next());
+                    }
+                }
+
+                return new Iterator0() {
+                    @Override
+                    public boolean hasNext() {
+                        return !queue.isEmpty();
+                    }
+
+                    @Override
+                    public Object next() {
+                        return queue.remove();
+                    }
+                };
+            }
+        });
+    }
+
+    public Stream sorted(final LambdaExpression comparator) {
+        return new Stream(this, new Operator() {
+
+            private PriorityQueue<Object> queue = null;
+
+            @Override
+            public Iterator<Object> iterator(final Iterator<Object> up) {
+                if (queue == null) {
+                    queue = new PriorityQueue<Object>(16, new Comparator<Object>() {
+                        @Override
+                        public int compare(Object o1, Object o2) {
+                            return (Integer) ELSupport.coerceToType(comparator.invoke(o1, o2), Integer.class);
+                        }
+                    });
+
+                    while (up.hasNext()) {
+                        queue.add(up.next());
+                    }
+                }
+
+                return new Iterator0() {
+                    @Override
+                    public boolean hasNext() {
+                        return !queue.isEmpty();
+                    }
+
+                    @Override
+                    public Object next() {
+                        return queue.remove();
+                    }
+                };
+            }
+        });
+    }
+
+    public Stream flatMap(final LambdaExpression mapper) {
+        return new Stream(this, new Operator() {
+            @Override
+            public Iterator<Object> iterator(final Iterator<Object> upstream) {
+                return new Iterator0() {
+                    Iterator<Object> iter = null;
+
+                    @Override
+                    public boolean hasNext() {
+                        while (true) {
+                            if (iter == null) {
+                                if (!upstream.hasNext()) {
+                                    return false;
+                                }
+                                Object mapped = mapper.invoke(upstream.next());
+                                if (!(mapped instanceof Stream)) {
+                                    throw new ELException("Expecting a Stream " + "from flatMap's mapper function.");
+                                }
+                                iter = ((Stream) mapped).iterator();
+                            } else {
+                                if (iter.hasNext()) {
+                                    return true;
+                                }
+                                iter = null;
+                            }
+                        }
+                    }
+
+                    @Override
+                    public Object next() {
+                        if (iter == null) {
+                            return null;
+                        }
+                        return iter.next();
+                    }
+                };
+            }
+        });
+    }
+
+    public Object reduce(Object base, LambdaExpression op) {
+        Iterator<Object> iter = iterator();
+        while (iter.hasNext()) {
+            base = op.invoke(base, iter.next());
+        }
+        return base;
+    }
+
+    public Optional reduce(LambdaExpression op) {
+        Iterator<Object> iter = iterator();
+        if (iter.hasNext()) {
+            Object base = iter.next();
+            while (iter.hasNext()) {
+                base = op.invoke(base, iter.next());
+            }
+            return new Optional(base);
+        }
+        return new Optional();
+    }
+
+    /*
+     * public Map<Object,Object> reduceBy(LambdaExpression classifier, LambdaExpression seed, LambdaExpression reducer) {
+     * Map<Object,Object> map = new HashMap<Object,Object>(); Iterator<Object> iter = iterator(); while (iter.hasNext()) {
+     * Object item = iter.next(); Object key = classifier.invoke(item); Object value = map.get(key); if (value == null) {
+     * value = seed.invoke(); } map.put(key, reducer.invoke(value, item)); } return map; }
+     */
+
+    public void forEach(LambdaExpression comsumer) {
+        Iterator<Object> iter = iterator();
+        while (iter.hasNext()) {
+            comsumer.invoke(iter.next());
+        }
+    }
+
+    /*
+     * public Map<Object,Collection<Object>> groupBy(LambdaExpression classifier) { Map<Object, Collection<Object>> map =
+     * new HashMap<Object, Collection<Object>>(); Iterator<Object> iter = iterator(); while (iter.hasNext()) { Object item =
+     * iter.next(); Object key = classifier.invoke(item); if (key == null) { throw new ELException("null key"); }
+     * Collection<Object> c = map.get(key); if (c == null) { c = new ArrayList<Object>(); map.put(key, c); } c.add(item); }
+     * return map; }
+     */
+    public boolean anyMatch(LambdaExpression predicate) {
+        Iterator<Object> iter = iterator();
+        while (iter.hasNext()) {
+            if ((Boolean) predicate.invoke(iter.next())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public boolean allMatch(LambdaExpression predicate) {
+        Iterator<Object> iter = iterator();
+        while (iter.hasNext()) {
+            if (!(Boolean) predicate.invoke(iter.next())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public boolean noneMatch(LambdaExpression predicate) {
+        Iterator<Object> iter = iterator();
+        while (iter.hasNext()) {
+            if ((Boolean) predicate.invoke(iter.next())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public Object[] toArray() {
+        Iterator<Object> iter = iterator();
+        ArrayList<Object> al = new ArrayList<Object>();
+        while (iter.hasNext()) {
+            al.add(iter.next());
+        }
+        return al.toArray();
+    }
+
+    public Object toList() {
+        Iterator<Object> iter = iterator();
+        ArrayList<Object> al = new ArrayList<Object>();
+        while (iter.hasNext()) {
+            al.add(iter.next());
+        }
+        return al;
+    }
+
+    /*
+     * public Object into(Object target) { if (! (target instanceof Collection)) { throw new
+     * ELException("The argument type for into operation mush be a Collection"); } Collection<Object> c =
+     * (Collection<Object>) target; Iterator<Object> iter = iterator(); while (iter.hasNext()) { c.add(iter.next()); }
+     * return c; }
+     */
+
+    public Optional findFirst() {
+        Iterator<Object> iter = iterator();
+        if (iter.hasNext()) {
+            return new Optional(iter.next());
+        } else {
+            return new Optional();
+        }
+    }
+
+    public Object sum() {
+        Number sum = Long.valueOf(0);
+        Iterator<Object> iter = iterator();
+        while (iter.hasNext()) {
+            sum = ELArithmetic.add(sum, iter.next());
+        }
+        return sum;
+    }
+
+    public Object count() {
+        long count = 0;
+        Iterator<Object> iter = iterator();
+        while (iter.hasNext()) {
+            count++;
+            iter.next();
+        }
+        return Long.valueOf(count);
+    }
+
+    public Optional min() {
+        Object min = null;
+        Iterator<Object> iter = iterator();
+        while (iter.hasNext()) {
+            Object item = iter.next();
+            if (min == null || ELSupport.compare(min, item) > 0) {
+                min = item;
+            }
+        }
+        if (min == null) {
+            return new Optional();
+        }
+        return new Optional(min);
+    }
+
+    public Optional max() {
+        Object max = null;
+        Iterator<Object> iter = iterator();
+        while (iter.hasNext()) {
+            Object item = iter.next();
+            if (max == null || ELSupport.compare(max, item) < 0) {
+                max = item;
+            }
+        }
+        if (max == null) {
+            return new Optional();
+        }
+        return new Optional(max);
+    }
+
+    public Optional min(final LambdaExpression comparator) {
+        Object min = null;
+        Iterator<Object> iter = iterator();
+        while (iter.hasNext()) {
+            Object item = iter.next();
+            if (min == null || ELSupport.compare(comparator.invoke(item, min), Long.valueOf(0)) < 0) {
+                min = item;
+            }
+        }
+        if (min == null) {
+            return new Optional();
+        }
+        return new Optional(min);
+    }
+
+    public Optional max(final LambdaExpression comparator) {
+        Object max = null;
+        Iterator<Object> iter = iterator();
+        while (iter.hasNext()) {
+            Object item = iter.next();
+            if (max == null || ELSupport.compare(comparator.invoke(max, item), Long.valueOf(0)) < 0) {
+                max = item;
+            }
+        }
+        if (max == null) {
+            return new Optional();
+        }
+        return new Optional(max);
+    }
+
+    public Optional average() {
+        Number sum = Long.valueOf(0);
+        long count = 0;
+        Iterator<Object> iter = iterator();
+        while (iter.hasNext()) {
+            count++;
+            sum = ELArithmetic.add(sum, iter.next());
+        }
+        if (count == 0) {
+            return new Optional();
+        }
+        return new Optional(ELArithmetic.divide(sum, count));
+    }
+
+    abstract class Iterator0 implements Iterator<Object> {
+        @Override
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    abstract class Iterator1 extends Iterator0 {
+
+        Iterator iter;
+
+        Iterator1(Iterator iter) {
+            this.iter = iter;
+        }
+
+        @Override
+        public boolean hasNext() {
+            return iter.hasNext();
+        }
+    }
+
+    abstract class Iterator2 extends Iterator1 {
+        private Object current;
+        private boolean yielded;
+
+        Iterator2(Iterator upstream) {
+            super(upstream);
+        }
+
+        @Override
+        public Object next() {
+            yielded = false;
+            return current;
+        }
+
+        @Override
+        public boolean hasNext() {
+            while ((!yielded) && iter.hasNext()) {
+                doItem(iter.next());
+            }
+            return yielded;
+        }
+
+        void yield(Object current) {
+            this.current = current;
+            yielded = true;
+        }
+
+        abstract void doItem(Object item);
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/stream/StreamELResolver.java b/impl/src/main/java/com/sun/el/stream/StreamELResolver.java
new file mode 100644
index 0000000..0963a55
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/stream/StreamELResolver.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.stream;
+
+import java.beans.FeatureDescriptor;
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Iterator;
+
+import javax.el.ELContext;
+import javax.el.ELResolver;
+
+/*
+ * This ELResolver intercepts method calls to a Collections, to provide
+ * support for collection operations.
+ */
+
+public class StreamELResolver extends ELResolver {
+
+    @Override
+    public Object invoke(final ELContext context, final Object base, final Object method, final Class<?>[] paramTypes, final Object[] params) {
+
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base instanceof Collection) {
+            @SuppressWarnings("unchecked")
+            Collection<Object> c = (Collection<Object>) base;
+            if ("stream".equals(method) && params.length == 0) {
+                context.setPropertyResolved(true);
+                return new Stream(c.iterator());
+            }
+        }
+        if (base.getClass().isArray()) {
+            if ("stream".equals(method) && params.length == 0) {
+                context.setPropertyResolved(true);
+                return new Stream(arrayIterator(base));
+            }
+        }
+        return null;
+    }
+
+    private static Iterator<Object> arrayIterator(final Object base) {
+        final int size = Array.getLength(base);
+        return new Iterator<Object>() {
+            int index = 0;
+            boolean yielded;
+            Object current;
+
+            @Override
+            public boolean hasNext() {
+                if ((!yielded) && index < size) {
+                    current = Array.get(base, index++);
+                    yielded = true;
+                }
+                return yielded;
+            }
+
+            @Override
+            public Object next() {
+                yielded = false;
+                return current;
+            }
+
+            @Override
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    /*
+     * private LambdaExpression getLambda(Object obj, String method) { if (obj == null || ! (obj instanceof
+     * LambdaExpression)) { throw new ELException ("When calling " + method + ", expecting an " +
+     * "EL lambda expression, but found " + obj); } return (LambdaExpression) obj; }
+     */
+    @Override
+    public Object getValue(ELContext context, Object base, Object property) {
+        return null;
+    }
+
+    @Override
+    public Class<?> getType(ELContext context, Object base, Object property) {
+        return null;
+    }
+
+    @Override
+    public void setValue(ELContext context, Object base, Object property, Object value) {
+    }
+
+    @Override
+    public boolean isReadOnly(ELContext context, Object base, Object property) {
+        return false;
+    }
+
+    @Override
+    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
+        return null;
+    }
+
+    @Override
+    public Class<?> getCommonPropertyType(ELContext context, Object base) {
+        return String.class;
+    }
+}
diff --git a/impl/src/main/java/com/sun/el/util/MessageFactory.java b/impl/src/main/java/com/sun/el/util/MessageFactory.java
new file mode 100644
index 0000000..356124b
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/util/MessageFactory.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.util;
+
+import java.text.MessageFormat;
+import java.util.ResourceBundle;
+
+/**
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public final class MessageFactory {
+
+    protected final static ResourceBundle bundle = ResourceBundle.getBundle("com.sun.el.Messages");
+
+    /**
+     *
+     */
+    public MessageFactory() {
+        super();
+    }
+
+    public static String get(final String key) {
+        return bundle.getString(key);
+    }
+
+    public static String get(final String key, final Object obj0) {
+        return getArray(key, new Object[] { obj0 });
+    }
+
+    public static String get(final String key, final Object obj0, final Object obj1) {
+        return getArray(key, new Object[] { obj0, obj1 });
+    }
+
+    public static String get(final String key, final Object obj0, final Object obj1, final Object obj2) {
+        return getArray(key, new Object[] { obj0, obj1, obj2 });
+    }
+
+    public static String get(final String key, final Object obj0, final Object obj1, final Object obj2, final Object obj3) {
+        return getArray(key, new Object[] { obj0, obj1, obj2, obj3 });
+    }
+
+    public static String get(final String key, final Object obj0, final Object obj1, final Object obj2, final Object obj3, final Object obj4) {
+        return getArray(key, new Object[] { obj0, obj1, obj2, obj3, obj4 });
+    }
+
+    public static String getArray(final String key, final Object[] objA) {
+        return MessageFormat.format(bundle.getString(key), objA);
+    }
+
+}
diff --git a/impl/src/main/java/com/sun/el/util/ReflectionUtil.java b/impl/src/main/java/com/sun/el/util/ReflectionUtil.java
new file mode 100644
index 0000000..3e4e48e
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/util/ReflectionUtil.java
@@ -0,0 +1,712 @@
+/*
+ * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package com.sun.el.util;
+
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.el.ELContext;
+import javax.el.ELException;
+import javax.el.MethodNotFoundException;
+import javax.el.PropertyNotFoundException;
+
+import com.sun.el.lang.ELSupport;
+
+/**
+ * Utilities for Managing Serialization and Reflection
+ *
+ * @author Jacob Hookom [jacob@hookom.net]
+ * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
+ */
+public class ReflectionUtil {
+
+    protected static final String[] EMPTY_STRING = new String[0];
+
+    protected static final String[] PRIMITIVE_NAMES = new String[] { "boolean", "byte", "char", "double", "float", "int", "long", "short", "void" };
+
+    protected static final Class[] PRIMITIVES = new Class[] { boolean.class, byte.class, char.class, double.class, float.class, int.class, long.class,
+            short.class, Void.TYPE };
+
+    /**
+     *
+     */
+    private ReflectionUtil() {
+        super();
+    }
+
+    public static Class forName(String name) throws ClassNotFoundException {
+        if (null == name || "".equals(name)) {
+            return null;
+        }
+        Class c = forNamePrimitive(name);
+        if (c == null) {
+            if (name.endsWith("[]")) {
+                String nc = name.substring(0, name.length() - 2);
+                c = Class.forName(nc, true, Thread.currentThread().getContextClassLoader());
+                c = Array.newInstance(c, 0).getClass();
+            } else {
+                c = Class.forName(name, true, Thread.currentThread().getContextClassLoader());
+            }
+        }
+        return c;
+    }
+
+    protected static Class forNamePrimitive(String name) {
+        if (name.length() <= 8) {
+            int p = Arrays.binarySearch(PRIMITIVE_NAMES, name);
+            if (p >= 0) {
+                return PRIMITIVES[p];
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Converts an array of Class names to Class types
+     *
+     * @param s
+     * @return The array of Classes
+     * @throws ClassNotFoundException
+     */
+    public static Class[] toTypeArray(String[] s) throws ClassNotFoundException {
+        if (s == null) {
+            return null;
+        }
+        Class[] c = new Class[s.length];
+        for (int i = 0; i < s.length; i++) {
+            c[i] = forName(s[i]);
+        }
+        return c;
+    }
+
+    /**
+     * Converts an array of Class types to Class names
+     *
+     * @param c
+     * @return The array of Classes
+     */
+    public static String[] toTypeNameArray(Class[] c) {
+        if (c == null) {
+            return null;
+        }
+        String[] s = new String[c.length];
+        for (int i = 0; i < c.length; i++) {
+            s[i] = c[i].getName();
+        }
+        return s;
+    }
+
+    /**
+     * @param base The base object
+     * @param property The property
+     * @return The PropertyDescriptor for the base with the given property
+     * @throws ELException
+     * @throws PropertyNotFoundException
+     */
+    public static PropertyDescriptor getPropertyDescriptor(Object base, Object property) throws ELException, PropertyNotFoundException {
+        String name = ELSupport.coerceToString(property);
+        PropertyDescriptor p = null;
+        try {
+            PropertyDescriptor[] desc = Introspector.getBeanInfo(base.getClass()).getPropertyDescriptors();
+            for (int i = 0; i < desc.length; i++) {
+                if (desc[i].getName().equals(name)) {
+                    return desc[i];
+                }
+            }
+        } catch (IntrospectionException ie) {
+            throw new ELException(ie);
+        }
+        throw new PropertyNotFoundException(MessageFactory.get("error.property.notfound", base, name));
+    }
+
+    /*
+     * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync.
+     */
+    public static Object invokeMethod(ELContext context, Method m, Object base, Object[] params) {
+
+        Object[] parameters = buildParameters(context, m.getParameterTypes(), m.isVarArgs(), params);
+        try {
+            return m.invoke(base, parameters);
+        } catch (IllegalAccessException iae) {
+            throw new ELException(iae);
+        } catch (IllegalArgumentException iae) {
+            throw new ELException(iae);
+        } catch (InvocationTargetException ite) {
+            throw new ELException(ite.getCause());
+        }
+    }
+
+    /*
+     * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync.
+     */
+    public static Method findMethod(Class<?> clazz, String methodName, Class<?>[] paramTypes, Object[] paramValues) {
+
+        if (clazz == null || methodName == null) {
+            throw new MethodNotFoundException(MessageFactory.get("error.method.notfound", clazz, methodName, paramString(paramTypes)));
+        }
+
+        if (paramTypes == null) {
+            paramTypes = getTypesFromValues(paramValues);
+        }
+
+        Method[] methods = clazz.getMethods();
+
+        List<Wrapper> wrappers = Wrapper.wrap(methods, methodName);
+
+        Wrapper result = findWrapper(clazz, wrappers, methodName, paramTypes, paramValues);
+
+        if (result == null) {
+            return null;
+        }
+        return getMethod(clazz, (Method) result.unWrap());
+    }
+
+    /*
+     * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync.
+     */
+    private static Wrapper findWrapper(Class<?> clazz, List<Wrapper> wrappers, String name, Class<?>[] paramTypes, Object[] paramValues) {
+
+        List<Wrapper> assignableCandidates = new ArrayList<Wrapper>();
+        List<Wrapper> coercibleCandidates = new ArrayList<Wrapper>();
+        List<Wrapper> varArgsCandidates = new ArrayList<Wrapper>();
+
+        int paramCount;
+        if (paramTypes == null) {
+            paramCount = 0;
+        } else {
+            paramCount = paramTypes.length;
+        }
+
+        for (Wrapper w : wrappers) {
+            Class<?>[] mParamTypes = w.getParameterTypes();
+            int mParamCount;
+            if (mParamTypes == null) {
+                mParamCount = 0;
+            } else {
+                mParamCount = mParamTypes.length;
+            }
+
+            // Check the number of parameters
+            if (!(paramCount == mParamCount || (w.isVarArgs() && paramCount >= mParamCount - 1))) {
+                // Method has wrong number of parameters
+                continue;
+            }
+
+            // Check the parameters match
+            boolean assignable = false;
+            boolean coercible = false;
+            boolean varArgs = false;
+            boolean noMatch = false;
+            for (int i = 0; i < mParamCount; i++) {
+                if (i == (mParamCount - 1) && w.isVarArgs()) {
+                    varArgs = true;
+                    // exact var array type match
+                    if (mParamCount == paramCount) {
+                        if (mParamTypes[i] == paramTypes[i]) {
+                            continue;
+                        }
+                    }
+
+                    // unwrap the array's component type
+                    Class<?> varType = mParamTypes[i].getComponentType();
+                    for (int j = i; j < paramCount; j++) {
+                        if (!isAssignableFrom(paramTypes[j], varType)
+                                && !(paramValues != null && j < paramValues.length && isCoercibleFrom(paramValues[j], varType))) {
+                            noMatch = true;
+                            break;
+                        }
+                    }
+                } else if (mParamTypes[i].equals(paramTypes[i])) {
+                } else if (isAssignableFrom(paramTypes[i], mParamTypes[i])) {
+                    assignable = true;
+                } else {
+                    if (paramValues == null || i >= paramValues.length) {
+                        noMatch = true;
+                        break;
+                    } else {
+                        if (isCoercibleFrom(paramValues[i], mParamTypes[i])) {
+                            coercible = true;
+                        } else {
+                            noMatch = true;
+                            break;
+                        }
+                    }
+                }
+            }
+            if (noMatch) {
+                continue;
+            }
+
+            if (varArgs) {
+                varArgsCandidates.add(w);
+            } else if (coercible) {
+                coercibleCandidates.add(w);
+            } else if (assignable) {
+                assignableCandidates.add(w);
+            } else {
+                // If a method is found where every parameter matches exactly,
+                // return it
+                return w;
+            }
+
+        }
+
+        String errorMsg = MessageFactory.get("error.method.ambiguous", clazz, name, paramString(paramTypes));
+        if (!assignableCandidates.isEmpty()) {
+            return findMostSpecificWrapper(assignableCandidates, paramTypes, false, errorMsg);
+        } else if (!coercibleCandidates.isEmpty()) {
+            return findMostSpecificWrapper(coercibleCandidates, paramTypes, true, errorMsg);
+        } else if (!varArgsCandidates.isEmpty()) {
+            return findMostSpecificWrapper(varArgsCandidates, paramTypes, true, errorMsg);
+        } else {
+            throw new MethodNotFoundException(MessageFactory.get("error.method.notfound", clazz, name, paramString(paramTypes)));
+        }
+
+    }
+
+    /*
+     * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync.
+     */
+    private static Wrapper findMostSpecificWrapper(List<Wrapper> candidates, Class<?>[] matchingTypes, boolean elSpecific, String errorMsg) {
+        List<Wrapper> ambiguouses = new ArrayList<Wrapper>();
+        for (Wrapper candidate : candidates) {
+            boolean lessSpecific = false;
+
+            Iterator<Wrapper> it = ambiguouses.iterator();
+            while (it.hasNext()) {
+                int result = isMoreSpecific(candidate, it.next(), matchingTypes, elSpecific);
+                if (result == 1) {
+                    it.remove();
+                } else if (result == -1) {
+                    lessSpecific = true;
+                }
+            }
+
+            if (!lessSpecific) {
+                ambiguouses.add(candidate);
+            }
+        }
+
+        if (ambiguouses.size() > 1) {
+            throw new MethodNotFoundException(errorMsg);
+        }
+
+        return ambiguouses.get(0);
+    }
+
+    /*
+     * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync.
+     */
+    private static int isMoreSpecific(Wrapper wrapper1, Wrapper wrapper2, Class<?>[] matchingTypes, boolean elSpecific) {
+        Class<?>[] paramTypes1 = wrapper1.getParameterTypes();
+        Class<?>[] paramTypes2 = wrapper2.getParameterTypes();
+
+        if (wrapper1.isVarArgs()) {
+            // JLS8 15.12.2.5 Choosing the Most Specific Method
+            int length = Math.max(Math.max(paramTypes1.length, paramTypes2.length), matchingTypes.length);
+            paramTypes1 = getComparingParamTypesForVarArgsMethod(paramTypes1, length);
+            paramTypes2 = getComparingParamTypesForVarArgsMethod(paramTypes2, length);
+
+            if (length > matchingTypes.length) {
+                Class<?>[] matchingTypes2 = new Class<?>[length];
+                System.arraycopy(matchingTypes, 0, matchingTypes2, 0, matchingTypes.length);
+                matchingTypes = matchingTypes2;
+            }
+        }
+
+        int result = 0;
+        for (int i = 0; i < paramTypes1.length; i++) {
+            if (paramTypes1[i] != paramTypes2[i]) {
+                int r2 = isMoreSpecific(paramTypes1[i], paramTypes2[i], matchingTypes[i], elSpecific);
+                if (r2 == 1) {
+                    if (result == -1) {
+                        return 0;
+                    }
+                    result = 1;
+                } else if (r2 == -1) {
+                    if (result == 1) {
+                        return 0;
+                    }
+                    result = -1;
+                } else {
+                    return 0;
+                }
+            }
+        }
+
+        if (result == 0) {
+            // The nature of bridge methods is such that it actually
+            // doesn't matter which one we pick as long as we pick
+            // one. That said, pick the 'right' one (the non-bridge
+            // one) anyway.
+            result = Boolean.compare(wrapper1.isBridge(), wrapper2.isBridge());
+        }
+
+        return result;
+    }
+
+    /*
+     * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync.
+     */
+    private static int isMoreSpecific(Class<?> type1, Class<?> type2, Class<?> matchingType, boolean elSpecific) {
+        type1 = getBoxingTypeIfPrimitive(type1);
+        type2 = getBoxingTypeIfPrimitive(type2);
+        if (type2.isAssignableFrom(type1)) {
+            return 1;
+        } else if (type1.isAssignableFrom(type2)) {
+            return -1;
+        } else {
+            if (elSpecific) {
+                /*
+                 * Number will be treated as more specific
+                 *
+                 * ASTInteger only return Long or BigInteger, no Byte / Short / Integer. ASTFloatingPoint also.
+                 *
+                 */
+                if (matchingType != null && Number.class.isAssignableFrom(matchingType)) {
+                    boolean b1 = Number.class.isAssignableFrom(type1) || type1.isPrimitive();
+                    boolean b2 = Number.class.isAssignableFrom(type2) || type2.isPrimitive();
+                    if (b1 && !b2) {
+                        return 1;
+                    } else if (b2 && !b1) {
+                        return -1;
+                    } else {
+                        return 0;
+                    }
+                }
+
+                return 0;
+            } else {
+                return 0;
+            }
+        }
+    }
+
+    /*
+     * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync.
+     */
+    private static Class<?> getBoxingTypeIfPrimitive(Class<?> clazz) {
+        if (clazz.isPrimitive()) {
+            if (clazz == Boolean.TYPE) {
+                return Boolean.class;
+            } else if (clazz == Character.TYPE) {
+                return Character.class;
+            } else if (clazz == Byte.TYPE) {
+                return Byte.class;
+            } else if (clazz == Short.TYPE) {
+                return Short.class;
+            } else if (clazz == Integer.TYPE) {
+                return Integer.class;
+            } else if (clazz == Long.TYPE) {
+                return Long.class;
+            } else if (clazz == Float.TYPE) {
+                return Float.class;
+            } else {
+                return Double.class;
+            }
+        } else {
+            return clazz;
+        }
+
+    }
+
+    /*
+     * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync.
+     */
+    private static Class<?>[] getComparingParamTypesForVarArgsMethod(Class<?>[] paramTypes, int length) {
+        Class<?>[] result = new Class<?>[length];
+        System.arraycopy(paramTypes, 0, result, 0, paramTypes.length - 1);
+        Class<?> type = paramTypes[paramTypes.length - 1].getComponentType();
+        for (int i = paramTypes.length - 1; i < length; i++) {
+            result[i] = type;
+        }
+
+        return result;
+    }
+
+    /*
+     * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync.
+     */
+    private static final String paramString(Class<?>[] types) {
+        if (types != null) {
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < types.length; i++) {
+                if (types[i] == null) {
+                    sb.append("null, ");
+                } else {
+                    sb.append(types[i].getName()).append(", ");
+                }
+            }
+            if (sb.length() > 2) {
+                sb.setLength(sb.length() - 2);
+            }
+            return sb.toString();
+        }
+        return null;
+    }
+
+    /*
+     * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync.
+     */
+    static boolean isAssignableFrom(Class<?> src, Class<?> target) {
+        // src will always be an object
+        // Short-cut. null is always assignable to an object and in EL null
+        // can always be coerced to a valid value for a primitive
+        if (src == null) {
+            return true;
+        }
+
+        target = getBoxingTypeIfPrimitive(target);
+
+        return target.isAssignableFrom(src);
+    }
+
+    /*
+     * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync.
+     */
+    private static boolean isCoercibleFrom(Object src, Class<?> target) {
+        // TODO: This isn't pretty but it works. Significant refactoring would
+        // be required to avoid the exception.
+        try {
+            ELSupport.coerceToType(src, target);
+        } catch (Exception e) {
+            return false;
+        }
+        return true;
+    }
+
+    /*
+     * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync.
+     */
+    private static Class<?>[] getTypesFromValues(Object[] values) {
+        if (values == null) {
+            return null;
+        }
+
+        Class<?> result[] = new Class<?>[values.length];
+        for (int i = 0; i < values.length; i++) {
+            if (values[i] == null) {
+                result[i] = null;
+            } else {
+                result[i] = values[i].getClass();
+            }
+        }
+        return result;
+    }
+
+    /*
+     * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync.
+     *
+     * Get a public method form a public class or interface of a given method. Note that if a PropertyDescriptor is obtained
+     * for a non-public class that implements a public interface, the read/write methods will be for the class, and
+     * therefore inaccessible. To correct this, a version of the same method must be found in a superclass or interface.
+     *
+     */
+    static Method getMethod(Class<?> type, Method m) {
+        if (m == null || Modifier.isPublic(type.getModifiers())) {
+            return m;
+        }
+        Class<?>[] inf = type.getInterfaces();
+        Method mp = null;
+        for (int i = 0; i < inf.length; i++) {
+            try {
+                mp = inf[i].getMethod(m.getName(), m.getParameterTypes());
+                mp = getMethod(mp.getDeclaringClass(), mp);
+                if (mp != null) {
+                    return mp;
+                }
+            } catch (NoSuchMethodException e) {
+                // Ignore
+            }
+        }
+        Class<?> sup = type.getSuperclass();
+        if (sup != null) {
+            try {
+                mp = sup.getMethod(m.getName(), m.getParameterTypes());
+                mp = getMethod(mp.getDeclaringClass(), mp);
+                if (mp != null) {
+                    return mp;
+                }
+            } catch (NoSuchMethodException e) {
+                // Ignore
+            }
+        }
+        return null;
+    }
+
+    /*
+     * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync.
+     */
+    static Constructor<?> getConstructor(Class<?> type, Constructor<?> c) {
+        if (c == null || Modifier.isPublic(type.getModifiers())) {
+            return c;
+        }
+        Constructor<?> cp = null;
+        Class<?> sup = type.getSuperclass();
+        if (sup != null) {
+            try {
+                cp = sup.getConstructor(c.getParameterTypes());
+                cp = getConstructor(cp.getDeclaringClass(), cp);
+                if (cp != null) {
+                    return cp;
+                }
+            } catch (NoSuchMethodException e) {
+                // Ignore
+            }
+        }
+        return null;
+    }
+
+    /*
+     * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync.
+     */
+    static Object[] buildParameters(ELContext context, Class<?>[] parameterTypes, boolean isVarArgs, Object[] params) {
+        Object[] parameters = null;
+        if (parameterTypes.length > 0) {
+            parameters = new Object[parameterTypes.length];
+            int paramCount = params == null ? 0 : params.length;
+            if (isVarArgs) {
+                int varArgIndex = parameterTypes.length - 1;
+                // First argCount-1 parameters are standard
+                for (int i = 0; (i < varArgIndex && i < paramCount); i++) {
+                    parameters[i] = context.convertToType(params[i], parameterTypes[i]);
+                }
+                // Last parameter is the varargs
+                if (parameterTypes.length == paramCount && parameterTypes[varArgIndex] == params[varArgIndex].getClass()) {
+                    parameters[varArgIndex] = params[varArgIndex];
+                } else {
+                    Class<?> varArgClass = parameterTypes[varArgIndex].getComponentType();
+                    final Object varargs = Array.newInstance(varArgClass, (paramCount - varArgIndex));
+                    for (int i = (varArgIndex); i < paramCount; i++) {
+                        Array.set(varargs, i - varArgIndex, context.convertToType(params[i], varArgClass));
+                    }
+                    parameters[varArgIndex] = varargs;
+                }
+            } else {
+                for (int i = 0; i < parameterTypes.length && i < paramCount; i++) {
+                    parameters[i] = context.convertToType(params[i], parameterTypes[i]);
+                }
+            }
+        }
+        return parameters;
+    }
+
+    /*
+     * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync.
+     */
+    private abstract static class Wrapper {
+
+        public static List<Wrapper> wrap(Method[] methods, String name) {
+            List<Wrapper> result = new ArrayList<>();
+            for (Method method : methods) {
+                if (method.getName().equals(name)) {
+                    result.add(new MethodWrapper(method));
+                }
+            }
+            return result;
+        }
+
+        public static List<Wrapper> wrap(Constructor<?>[] constructors) {
+            List<Wrapper> result = new ArrayList<>();
+            for (Constructor<?> constructor : constructors) {
+                result.add(new ConstructorWrapper(constructor));
+            }
+            return result;
+        }
+
+        public abstract Object unWrap();
+
+        public abstract Class<?>[] getParameterTypes();
+
+        public abstract boolean isVarArgs();
+
+        public abstract boolean isBridge();
+    }
+
+    /*
+     * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync.
+     */
+    private static class MethodWrapper extends Wrapper {
+        private final Method m;
+
+        public MethodWrapper(Method m) {
+            this.m = m;
+        }
+
+        @Override
+        public Object unWrap() {
+            return m;
+        }
+
+        @Override
+        public Class<?>[] getParameterTypes() {
+            return m.getParameterTypes();
+        }
+
+        @Override
+        public boolean isVarArgs() {
+            return m.isVarArgs();
+        }
+
+        @Override
+        public boolean isBridge() {
+            return m.isBridge();
+        }
+    }
+
+    /*
+     * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync.
+     */
+    private static class ConstructorWrapper extends Wrapper {
+        private final Constructor<?> c;
+
+        public ConstructorWrapper(Constructor<?> c) {
+            this.c = c;
+        }
+
+        @Override
+        public Object unWrap() {
+            return c;
+        }
+
+        @Override
+        public Class<?>[] getParameterTypes() {
+            return c.getParameterTypes();
+        }
+
+        @Override
+        public boolean isVarArgs() {
+            return c.isVarArgs();
+        }
+
+        @Override
+        public boolean isBridge() {
+            return false;
+        }
+    }
+
+}
diff --git a/parent-pom/pom.xml b/parent-pom/pom.xml
new file mode 100644
index 0000000..26c74f5
--- /dev/null
+++ b/parent-pom/pom.xml
@@ -0,0 +1,150 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.glassfish.web</groupId>
+    <artifactId>el</artifactId>
+    <version>2.2.1-SNAPSHOT</version>
+    <packaging>pom</packaging>
+    <name>Expression Language related modules</name>
+
+    <properties>
+        <findbugs.version>2.3.1</findbugs.version>
+        <findbugs.exclude />
+        <findbugs.threshold>High</findbugs.threshold>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                    <configuration>
+                        <source>1.5</source>
+                        <target>1.5</target>
+                    </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-source-plugin</artifactId>
+                <version> 2.1 </version>
+                <configuration>
+                    <includePom>true</includePom>
+                </configuration>
+                <executions>
+                    <execution>
+                       <id>attach-sources</id>
+                       <goals>
+                           <goal>jar-no-fork</goal> 
+                       </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+              <groupId>org.apache.maven.plugins</groupId>
+              <artifactId>maven-javadoc-plugin</artifactId>
+              <executions>
+                  <execution>
+                      <id>attach-javadocs</id>
+                      <goals>
+                          <goal>jar</goal>
+                      </goals>
+                 </execution>
+              </executions>
+           </plugin>
+           <plugin>
+              <groupId>org.codehaus.mojo</groupId>
+              <artifactId>findbugs-maven-plugin</artifactId>
+              <version>${findbugs.version}</version>
+              <configuration>
+                  <threshold>${findbugs.threshold}</threshold>
+                  <excludeFilterFile>${findbugs.exclude}</excludeFilterFile>
+                  <findbugsXmlOutput>true</findbugsXmlOutput>
+                  <findbugsXmlWithMessages>true</findbugsXmlWithMessages>
+              </configuration>
+           </plugin>
+        </plugins>
+        <extensions>
+            <extension>
+                <groupId>org.jvnet.wagon-svn</groupId>
+                <artifactId>wagon-svn</artifactId>
+                <version>1.12</version>
+            </extension>
+            <extension>
+                 <groupId>org.apache.maven.wagon</groupId>
+                 <artifactId>wagon-webdav</artifactId>
+                 <version>1.0-beta-2</version>
+            </extension> 
+        </extensions>
+    </build>
+
+    <reporting>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>findbugs-maven-plugin</artifactId>
+                <version>2.1</version>
+                <configuration>
+                    <threshold>High</threshold>
+                    <excludeFilterFile>${findbugs.exclude}</excludeFilterFile>
+                </configuration>
+            </plugin>
+        </plugins>
+    </reporting>
+
+    <scm>
+        <connection>scm:svn:svn+ssh://janey@svn.java.net/uel~svn/trunk</connection>
+        <developerConnection>scm:svn:svn+ssh://janey@svn.java.net/uel~svn/trunk</developerConnection>
+        <url>svn+ssh://janey@svn.java.net/uel~svn/trunk/</url>
+    </scm>
+
+    <repositories>
+        <repository>
+            <id>glassfish-repository</id>
+            <url>http://download.java.net/maven/glassfish</url>
+        </repository>
+        <repository>
+            <id>maven2-repository.dev.java.net</id>
+            <name>Java.net Repository for Maven</name>
+            <url>http://download.java.net/maven/2</url>
+        </repository>
+    </repositories>
+    <pluginRepositories>
+        <pluginRepository>
+            <id>glassfish-repository</id>
+            <name>Java.net Repository for Maven 2</name>
+            <url>http://download.java.net/maven/glassfish</url>
+            <snapshots>
+                <updatePolicy>never</updatePolicy>
+            </snapshots>
+        </pluginRepository>
+    </pluginRepositories>
+    <distributionManagement>
+        <site>
+            <id>java.net-el</id>
+            <url>java-net:/uel~svn/trunk/repo/</url>
+        </site>
+        <repository>
+            <uniqueVersion>false</uniqueVersion>
+            <id>java.net-maven2-repository</id>
+            <url>java-net:/maven2-repository~svn/trunk/repository/</url>
+        </repository>
+    </distributionManagement>
+</project>
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..1ae64c0
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,328 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 1997, 2018 Oracle and/or its affiliates and others.
+    All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.eclipse.ee4j</groupId>
+        <artifactId>project</artifactId>
+        <version>1.0.7</version>
+    </parent>
+
+    <groupId>org.glassfish</groupId>
+    <artifactId>jakarta.el</artifactId>
+    <version>3.0.4</version>
+    
+    <name>Jakarta Expression Language 3.0</name>
+    <description>
+        Jakarta Expression Language provides a specification document, API, reference implementation and TCK 
+        that describes an expression language for Java applications.
+    </description>
+    <url>https://projects.eclipse.org/projects/ee4j.el</url>
+
+    <properties>
+        <!-- the bundle build number must be the same as the maven number -->
+        <bundle.version>${project.version}</bundle.version>
+        <!-- The most current api version -->
+        <spec.version>3.0</spec.version>
+        <extensionName>javax.el</extensionName>
+        <bundle.symbolicName>com.sun.el.javax.el</bundle.symbolicName>
+        <vendorName>Oracle Corporation</vendorName>
+        <findbugs.version>2.5.2</findbugs.version>
+        <findbugs.exclude>${project.basedir}/exclude.xml</findbugs.exclude>
+        <findbugs.threshold>High</findbugs.threshold>
+    </properties>
+    
+    <developers>
+        <developer>
+            <id>yaminikb</id>
+            <name>Yamini K B</name>
+            <organization>Oracle Corporation</organization>
+            <organizationUrl>http://www.oracle.com/</organizationUrl>
+        </developer>
+    </developers>
+
+    <contributors>
+        <contributor>
+            <name>Kin-man Chung</name>
+        </contributor>
+    </contributors>
+
+    <issueManagement>
+        <system>github</system>
+        <url>https://github.com/eclipse-ee4j/el-ri/issues</url>
+    </issueManagement>
+
+    <licenses>
+        <license>
+            <name>EPL 2.0</name>
+            <url>http://www.eclipse.org/legal/epl-2.0</url>
+            <distribution>repo</distribution>
+        </license>
+        <license>
+            <name>GPL2 w/ CPE</name>
+            <url>https://www.gnu.org/software/classpath/license.html</url>
+            <distribution>repo</distribution>
+        </license>
+    </licenses>
+    
+    <mailingLists>
+        <mailingList>
+            <name>Jakarta Expression Language mailing list</name>
+            <post>el-dev@eclipse.org</post>
+            <subscribe>https://dev.eclipse.org/mailman/listinfo/el-dev</subscribe>
+            <unsubscribe>https://dev.eclipse.org/mailman/listinfo/el-dev</unsubscribe>
+            <archive>https://dev.eclipse.org/mhonarc/lists/el-dev</archive>
+        </mailingList>
+    </mailingLists>
+    
+    <scm>
+        <connection>scm:git:https://github.com/eclipse-ee4j/el-ri.git</connection>
+        <developerConnection>
+            scm:git:git@github.com:eclipse-ee4j/el-ri.git
+        </developerConnection>
+        <url>https://github.com/eclipse-ee4j/el-ri</url>
+        <tag>HEAD</tag>
+    </scm>
+    
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.12</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>api/src/main/java</directory>
+                <includes>
+                    <include>**/*.properties</include>
+                    <include>**/*.xml</include>
+                </includes>
+            </resource>
+            <resource>
+                <directory>impl/src/main/java</directory>
+                <includes>
+                    <include>**/*.properties</include>
+                    <include>**/*.xml</include>
+                </includes>
+            </resource>
+            <resource>
+                <directory>${project.basedir}</directory>
+                <includes>
+                    <include>LICENSE.md</include>
+                    <include>NOTICE.md</include>
+                </includes>
+                <targetPath>META-INF</targetPath>
+            </resource>
+        </resources>
+    
+        <plugins>
+            <!-- Use this to include both the api and impl sources -->
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <version>1.1</version>
+                <executions>
+                    <execution>
+                        <id>add-source</id>
+                        <phase>generate-sources</phase>
+                        <goals>
+                            <goal>add-source</goal>
+                        </goals>
+                        <configuration>
+                            <sources>
+                                <source>api/src/main/java</source>
+                                <source>impl/src/main/java</source>
+                            </sources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            
+            <!-- 
+                Configure maven-bundle-plugin to generate OSGi manifest. 
+                
+                Please note: we use the manifest goal only and not the bundle goal. 
+                The bundle goal can lead to very surprising results if the package names are not correctly specified. 
+                So, we use the jar plugin to generate the jar. 
+            -->
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <version>1.4.3</version>
+                <configuration>
+                    <instructions>
+                        <Bundle-SymbolicName>${bundle.symbolicName}</Bundle-SymbolicName>
+                        <Bundle-Description>
+                            Expression Language ${spec.version} API and Implementation
+
+                        </Bundle-Description>
+                        <Bundle-Version>${bundle.version}</Bundle-Version>
+                        <Extension-Name>${extensionName}</Extension-Name>
+                        <Specification-Version>${spec.version}</Specification-Version>
+                        <Specification-Vendor>${vendorName}</Specification-Vendor>
+                        <Implementation-Version>${project.version}</Implementation-Version>
+                        <Implementation-Vendor>${vendorName}</Implementation-Vendor>
+                    </instructions>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>bundle-manifest</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>manifest</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            
+            <!-- Adds the manifest file created by the org.apache.felix:maven-bundle-plugin -->
+            <plugin>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>2.4</version>
+                <configuration>
+                    <archive>
+                        <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
+                    </archive>
+                </configuration>
+            </plugin>
+            
+            <!-- Restricts the Java version to 1.7 -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.0</version>
+                <configuration>
+                    <source>1.7</source>
+                    <target>1.7</target>
+                    <compilerArgument>-Xlint:unchecked</compilerArgument>
+                </configuration>
+            </plugin>
+
+            <!-- Creates the source jar -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-source-plugin</artifactId>
+                <version> 2.2.1 </version>
+                <configuration>
+                    <includePom>true</includePom>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>attach-sources</id>
+                        <goals>
+                            <goal>jar-no-fork</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <!-- Creates the javadoc jar -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>3.0.1</version>
+                <executions>
+                    <execution>
+                        <id>attach-javadocs</id>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                        <configuration>
+                            <source>1.7</source>
+                            <sourcepath>api/src;impl/src</sourcepath>
+                            <additionalJOption>-Xdoclint:none</additionalJOption>
+                            <links>
+                                <link>https://javaee.github.io/javaee-spec/javadocs/</link>
+                            </links>
+                            <groups>
+                                <group>
+                                    <title>Jakarta Expression Language 3.0 API and Implementation</title>
+                                    <packages>com.sun.el</packages>
+                                </group>
+                            </groups>
+                            <bottom> Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved. Use is subject to license terms. </bottom>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            
+            <!-- Use ant to manually invoke javacc, as this required is very infrequently <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>javacc-maven-plugin</artifactId> 
+                <version>2.6</version> <executions> <execution> <id>jjtree-javacc</id> <goals> <goal>jjtree-javacc</goal> </goals> <configuration> <sourceDirectory>src/main/java/com/sun/el/parser</sourceDirectory> 
+                <outputDirectory>src/main/java/com/sun/el/parser</outputDirectory> </configuration> </execution> </executions> </plugin> -->
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>findbugs-maven-plugin</artifactId>
+                <version>${findbugs.version}</version>
+                <configuration>
+                    <threshold>${findbugs.threshold}</threshold>
+                    <excludeFilterFile>${findbugs.exclude}</excludeFilterFile>
+                    <findbugsXmlOutput>true</findbugsXmlOutput>
+                    <findbugsXmlWithMessages>true</findbugsXmlWithMessages>
+                </configuration>
+            </plugin>
+            
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>2.7.1</version>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.apache.maven.surefire</groupId>
+                        <artifactId>surefire-junit47</artifactId>
+                        <version>2.7.1</version>
+                    </dependency>
+                </dependencies>
+                <configuration>
+                    <forkMode>never</forkMode>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    
+    <reporting>
+        <plugins>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>findbugs-maven-plugin</artifactId>
+                <version>${findbugs.version}</version>
+                <configuration>
+                    <threshold>${findbugs.threshold}</threshold>
+                    <excludeFilterFile>${findbugs.exclude}</excludeFilterFile>
+                </configuration>
+            </plugin>
+             <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>3.0.1</version>
+                <configuration>
+                    <excludePackageNames>com.sun.el.parser</excludePackageNames>
+                    <sourceFileExcludes>
+                        <sourceFileExclude>**/parser/*.java</sourceFileExclude>
+                    </sourceFileExcludes>
+                </configuration>
+            </plugin>
+        </plugins>
+    </reporting>
+   
+</project>
diff --git a/spec/README.md b/spec/README.md
new file mode 100644
index 0000000..70f25b2
--- /dev/null
+++ b/spec/README.md
@@ -0,0 +1,22 @@
+Jakarta Expression Language Specification
+============================
+
+This project generates the Jakarta Expression Language Specification.
+
+Building
+--------
+
+Prerequisites:
+
+* JDK8+
+* Maven 3.0.3+
+
+Run the full build:
+
+`mvn install`
+
+Locate the html files:
+- `target/generated-docs/expression-language-spec-<version>.html`
+
+Locate the PDF files:
+- `target/generated-docs/expression-language-spec-<version>.pdf`
diff --git a/spec/assembly.xml b/spec/assembly.xml
new file mode 100644
index 0000000..7f30954
--- /dev/null
+++ b/spec/assembly.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!--
+ 
+    Copyright (c) 2019 Contributors to the Eclipse Foundation.
+ 
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+ 
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+ 
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ 
+-->
+
+<assembly>
+    <id>spec</id>
+    <formats>
+    <format>zip</format>
+    </formats>
+    <baseDirectory>expression-language-spec</baseDirectory>
+    <fileSets>
+        <fileSet>
+            <directory>target/generated-docs</directory>
+            <outputDirectory></outputDirectory>
+        </fileSet>
+    </fileSets>
+</assembly>
diff --git a/spec/pom.xml b/spec/pom.xml
new file mode 100644
index 0000000..8eacf9b
--- /dev/null
+++ b/spec/pom.xml
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Copyright (c) 2019 Contributors to the Eclipse Foundation.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion>
+    
+    <parent>
+        <groupId>org.eclipse.ee4j</groupId>
+        <artifactId>project</artifactId>
+        <version>1.0.5</version>
+        <relativePath/>
+    </parent>
+    
+    <groupId>org.eclipse.ee4j.expression-language</groupId>
+    <artifactId>expression-language-spec</artifactId>
+    <version>3.0</version>
+    <packaging>pom</packaging>
+    <name>Jakarta Expression Language Specification</name>
+
+    <properties>
+        <site.output.dir>${project.build.directory}/staging</site.output.dir>
+        <maven.site.skip>true</maven.site.skip>
+        <asciidoctor.maven.plugin.version>1.5.7.1</asciidoctor.maven.plugin.version>
+        <asciidoctorj.version>1.6.2</asciidoctorj.version>
+        <asciidoctorj.pdf.version>1.5.0-alpha.16</asciidoctorj.pdf.version>
+        <jruby.version>9.2.6.0</jruby.version>
+        <!-- status: DRAFT, BETA, etc., or blank for final -->
+        <status>DRAFT</status>
+        <maven.build.timestamp.format>MMMM dd, yyyy</maven.build.timestamp.format>
+        <revisiondate>${maven.build.timestamp}</revisiondate>
+    </properties>
+
+    <build>
+        <defaultGoal>package</defaultGoal>
+        <plugins>
+            <plugin>
+                <groupId>org.asciidoctor</groupId>
+                <artifactId>asciidoctor-maven-plugin</artifactId>
+                <version>${asciidoctor.maven.plugin.version}</version>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.jruby</groupId>
+                        <artifactId>jruby-complete</artifactId>
+                        <version>${jruby.version}</version>
+                    </dependency>
+                    <dependency>
+                        <groupId>org.asciidoctor</groupId>
+                        <artifactId>asciidoctorj</artifactId>
+                        <version>${asciidoctorj.version}</version>
+                    </dependency>
+                    <dependency>
+                        <groupId>org.asciidoctor</groupId>
+                        <artifactId>asciidoctorj-pdf</artifactId>
+                        <version>${asciidoctorj.pdf.version}</version>
+                    </dependency>
+                </dependencies>
+                <executions>
+                    <execution>
+                        <id>asciidoc-to-html</id>
+                        <phase>generate-resources</phase>
+                        <goals>
+                            <goal>process-asciidoc</goal>
+                        </goals>
+                        <configuration>
+                            <backend>html5</backend>
+                            <outputFile>${project.build.directory}/generated-docs/expression-language-spec-${project.version}.html</outputFile>
+                            <attributes>
+                                <doctype>book</doctype>
+                                <status>${status}</status>
+                                <data-uri />
+                                <icons>font</icons>
+                                <toc>left</toc>
+                                <icons>font</icons>
+                                <sectanchors>true</sectanchors>
+                                <idprefix />
+                                <idseparator>-</idseparator>
+                                <docinfo1>true</docinfo1>
+                            </attributes>
+                        </configuration>
+                    </execution>
+                    <execution>
+                        <id>asciidoc-to-pdf</id>
+                        <phase>generate-resources</phase>
+                        <goals>
+                            <goal>process-asciidoc</goal>
+                        </goals>
+                        <configuration>
+                            <backend>pdf</backend>
+                            <outputFile>${project.build.directory}/generated-docs/expression-language-spec-${project.version}.pdf</outputFile>
+                            <attributes>
+                                <pdf-stylesdir>${project.basedir}/src/main/theme</pdf-stylesdir>
+                                <pdf-style>jakartaee</pdf-style>
+                                <doctype>book</doctype>
+                                <status>${status}</status>
+                                <data-uri />
+                                <icons>font</icons>
+                                <pagenums />
+                                <toc />
+                                <icons>font</icons>
+                                <sectanchors>true</sectanchors>
+                                <idprefix />
+                                <idseparator>-</idseparator>
+                                <docinfo1>true</docinfo1>
+                                <embedAssets>true</embedAssets>
+                            </attributes>
+                        </configuration>
+                    </execution>
+                </executions>
+                <configuration>
+                    <sourceDocumentName>expression-language-spec.adoc</sourceDocumentName>
+                    <sourceHighlighter>coderay</sourceHighlighter>
+                    <attributes>
+                        <revnumber>${project.version}</revnumber>
+                        <revremark>${status}</revremark>
+                        <revdate>${revisiondate}</revdate>
+                    </attributes>
+                </configuration>
+            </plugin>
+            
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-release-plugin</artifactId>
+                <version>2.5.2</version>
+                <configuration>
+                    <mavenExecutorId>forked-path</mavenExecutorId>
+                    <useReleaseProfile>false</useReleaseProfile>
+                    <arguments>${release.arguments}</arguments>
+                </configuration>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.apache.maven.scm</groupId>
+                        <artifactId>maven-scm-provider-gitexe</artifactId>
+                        <version>1.9.4</version>
+                    </dependency>
+                </dependencies>
+            </plugin>
+
+            <!--
+                This is the rule that builds the zip file for download.
+            -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <version>3.1.1</version>
+                <inherited>false</inherited>
+                <executions>
+                    <execution>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                        <configuration>
+                            <appendAssemblyId>false</appendAssemblyId>
+                            <descriptors>
+                                <descriptor>assembly.xml</descriptor>
+                            </descriptors>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/spec/src/main/asciidoc/expression-language-spec.adoc b/spec/src/main/asciidoc/expression-language-spec.adoc
new file mode 100644
index 0000000..edf075d
--- /dev/null
+++ b/spec/src/main/asciidoc/expression-language-spec.adoc
@@ -0,0 +1,28 @@
+//
+// Copyright (c) 2017, 2019 Contributors to the Eclipse Foundation
+//
+
+= Jakarta Expression Language
+:authors: Jakarta Expression Language Team, https://projects.eclipse.org/projects/ee4j.el
+:email: https://dev.eclipse.org/mailman/listinfo/el-dev
+:version-label!:
+:doctype: book
+:license: Eclipse Foundation Specification License v1.0
+:source-highlighter: coderay
+:toc: left
+:toclevels: 4
+:sectnumlevels: 4
+:sectanchors:
+ifdef::backend-pdf[]
+:pagenums:
+:numbered:
+:title-logo-image: image:jakarta_ee_logo_schooner_color_stacked_default.png[pdfwidth=4.25in,align=right]
+endif::[]
+
+// == License
+:sectnums!:
+include::license-efsl.adoc[]
+
+// == Scope
+:sectnums:
+include::scope.adoc[]
diff --git a/spec/src/main/asciidoc/images/jakarta_ee_logo_schooner_color_stacked_default.png b/spec/src/main/asciidoc/images/jakarta_ee_logo_schooner_color_stacked_default.png
new file mode 100644
index 0000000..97b46ce
--- /dev/null
+++ b/spec/src/main/asciidoc/images/jakarta_ee_logo_schooner_color_stacked_default.png
Binary files differ
diff --git a/spec/src/main/asciidoc/license-efsl.adoc b/spec/src/main/asciidoc/license-efsl.adoc
new file mode 100644
index 0000000..d92d677
--- /dev/null
+++ b/spec/src/main/asciidoc/license-efsl.adoc
@@ -0,0 +1,74 @@
+[subs="normal"]
+....
+Specification: {doctitle}
+
+Version: {revnumber}
+
+Status: {revremark}
+
+Release: {revdate}
+....
+
+Copyright (c) 2019 Eclipse Foundation.
+
+=== Eclipse Foundation Specification License
+
+By using and/or copying this document, or the Eclipse Foundation
+document from which this statement is linked, you (the licensee) agree
+that you have read, understood, and will comply with the following
+terms and conditions:
+
+Permission to copy, and distribute the contents of this document, or
+the Eclipse Foundation document from which this statement is linked, in
+any medium for any purpose and without fee or royalty is hereby
+granted, provided that you include the following on ALL copies of the
+document, or portions thereof, that you use:
+
+* link or URL to the original Eclipse Foundation document.
+* All existing copyright notices, or if one does not exist, a notice
+  (hypertext is preferred, but a textual representation is permitted)
+  of the form: "Copyright (c) [$date-of-document]
+  Eclipse Foundation, Inc. <<url to this license>>"
+
+Inclusion of the full text of this NOTICE must be provided. We
+request that authorship attribution be provided in any software,
+documents, or other items or products that you create pursuant to the
+implementation of the contents of this document, or any portion
+thereof.
+
+No right to create modifications or derivatives of Eclipse Foundation
+documents is granted pursuant to this license, except anyone may
+prepare and distribute derivative works and portions of this document
+in software that implements the specification, in supporting materials
+accompanying such software, and in documentation of such software,
+PROVIDED that all such works include the notice below. HOWEVER, the
+publication of derivative works of this document for use as a technical
+specification is expressly prohibited.
+
+The notice is:
+
+"Copyright (c) 2018 Eclipse Foundation. This software or
+document includes material copied from or derived from [title and URI
+of the Eclipse Foundation specification document]."
+
+==== Disclaimers
+
+THIS DOCUMENT IS PROVIDED &quot;AS IS,&quot; AND THE COPYRIGHT
+HOLDERS AND THE ECLIPSE FOUNDATION MAKE NO REPRESENTATIONS OR
+WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
+NON-INFRINGEMENT, OR TITLE; THAT THE CONTENTS OF THE DOCUMENT ARE
+SUITABLE FOR ANY PURPOSE; NOR THAT THE IMPLEMENTATION OF SUCH CONTENTS
+WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR
+OTHER RIGHTS.
+
+THE COPYRIGHT HOLDERS AND THE ECLIPSE FOUNDATION WILL NOT BE LIABLE
+FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT
+OF ANY USE OF THE DOCUMENT OR THE PERFORMANCE OR IMPLEMENTATION OF THE
+CONTENTS THEREOF.
+
+The name and trademarks of the copyright holders or the Eclipse
+Foundation may NOT be used in advertising or publicity pertaining to
+this document or its contents without specific, written prior
+permission. Title to copyright in this document will at all times
+remain with copyright holders.
diff --git a/spec/src/main/asciidoc/scope.adoc b/spec/src/main/asciidoc/scope.adoc
new file mode 100644
index 0000000..778b8b6
--- /dev/null
+++ b/spec/src/main/asciidoc/scope.adoc
@@ -0,0 +1,3 @@
+== Specification Scope
+
+Jakarta Expression Language defines an expression language for Java applications.
diff --git a/spec/src/main/theme/jakartaee-theme.yml b/spec/src/main/theme/jakartaee-theme.yml
new file mode 100644
index 0000000..6092a2f
--- /dev/null
+++ b/spec/src/main/theme/jakartaee-theme.yml
@@ -0,0 +1,299 @@
+#
+# Following is the asciidoctor-pdf default theme [1], with small
+# customizations, mostly for header and footer, marked "EE".
+#
+# [1] https://github.com/asciidoctor/asciidoctor-pdf/blob/master/data/themes/default-theme.yml
+#
+font:
+  catalog:
+    # Noto Serif supports Latin, Latin-1 Supplement, Latin Extended-A, Greek, Cyrillic, Vietnamese & an assortment of symbols
+    Noto Serif:
+      normal: notoserif-regular-subset.ttf
+      bold: notoserif-bold-subset.ttf
+      italic: notoserif-italic-subset.ttf
+      bold_italic: notoserif-bold_italic-subset.ttf
+    # M+ 1mn supports ASCII and the circled numbers used for conums
+    M+ 1mn:
+      normal: mplus1mn-regular-ascii-conums.ttf
+      bold: mplus1mn-bold-ascii.ttf
+      italic: mplus1mn-italic-ascii.ttf
+      bold_italic: mplus1mn-bold_italic-ascii.ttf
+    # M+ 1p supports Latin, Latin-1 Supplement, Latin Extended, Greek, Cyrillic, Vietnamese, Japanese & an assortment of symbols
+    # It also provides arrows for ->, <-, => and <= replacements in case these glyphs are missing from font
+    M+ 1p Fallback:
+      normal: mplus1p-regular-fallback.ttf
+      bold: mplus1p-regular-fallback.ttf
+      italic: mplus1p-regular-fallback.ttf
+      bold_italic: mplus1p-regular-fallback.ttf
+  fallbacks:
+    - M+ 1p Fallback
+page:
+  background_color: ffffff
+  layout: portrait
+  margin: [0.5in, 0.67in, 0.67in, 0.67in]
+  # margin_inner and margin_outer keys are used for recto/verso print margins when media=prepress
+  margin_inner: 0.75in
+  margin_outer: 0.59in
+  #size: A4                                     # EE
+  size: Letter                                  # EE
+base:
+  align: justify
+  # color as hex string (leading # is optional)
+  font_color: 333333
+  # color as RGB array
+  #font_color: [51, 51, 51]
+  # color as CMYK array (approximated)
+  #font_color: [0, 0, 0, 0.92]
+  #font_color: [0, 0, 0, 92%]
+  font_family: Noto Serif
+  # choose one of these font_size/line_height_length combinations
+  #font_size: 14
+  #line_height_length: 20
+  #font_size: 11.25
+  #line_height_length: 18
+  #font_size: 11.2
+  #line_height_length: 16
+  font_size: 10.5
+  #line_height_length: 15
+  # correct line height for Noto Serif metrics
+  line_height_length: 12
+  #font_size: 11.25
+  #line_height_length: 18
+  line_height: $base_line_height_length / $base_font_size
+  font_size_large: round($base_font_size * 1.25)
+  font_size_small: round($base_font_size * 0.85)
+  font_size_min: $base_font_size * 0.75
+  font_style: normal
+  border_color: eeeeee
+  border_radius: 4
+  border_width: 0.5
+# FIXME vertical_rhythm is weird; we should think in terms of ems
+#vertical_rhythm: $base_line_height_length * 2 / 3
+# correct line height for Noto Serif metrics (comes with built-in line height)
+vertical_rhythm: $base_line_height_length
+horizontal_rhythm: $base_line_height_length
+# QUESTION should vertical_spacing be block_spacing instead?
+vertical_spacing: $vertical_rhythm
+link:
+  font_color: 428bca
+# literal is currently used for inline monospaced in prose and table cells
+literal:
+  font_color: b12146
+  font_family: M+ 1mn
+menu_caret_content: " <font size=\"1.15em\"><color rgb=\"b12146\">\u203a</color></font> "
+heading:
+  align: left
+  #font_color: 181818
+  font_color: $base_font_color
+  font_family: $base_font_family
+  font_style: bold
+  # h1 is used for part titles (book doctype) or the doctitle (article doctype)
+  #h1_font_size: floor($base_font_size * 2.6) # EE
+  h1_font_size: floor($base_font_size * 2.5) # EE, squeeze title onto one line
+  # h2 is used for chapter titles (book doctype only)
+  h2_font_size: floor($base_font_size * 2.15)
+  h3_font_size: round($base_font_size * 1.7)
+  h4_font_size: $base_font_size_large
+  h5_font_size: $base_font_size
+  h6_font_size: $base_font_size_small
+  #line_height: 1.4
+  # correct line height for Noto Serif metrics (comes with built-in line height)
+  line_height: 1
+  margin_top: $vertical_rhythm * 0.4
+  margin_bottom: $vertical_rhythm * 0.9
+title_page:
+  align: right
+  logo:
+    top: 10%
+  title:
+    top: 55%
+    font_size: $heading_h1_font_size
+    font_color: 999999
+    line_height: 0.9
+  subtitle:
+    font_size: $heading_h3_font_size
+    font_style: bold_italic
+    line_height: 1
+  authors:
+    margin_top: $base_font_size * 1.25
+    font_size: $base_font_size_large
+    font_color: 181818
+  revision:
+    margin_top: $base_font_size * 1.25
+block:
+  margin_top: 0
+  margin_bottom: $vertical_rhythm
+caption:
+  align: left
+  font_size: $base_font_size * 0.95
+  font_style: italic
+  # FIXME perhaps set line_height instead of / in addition to margins?
+  margin_inside: $vertical_rhythm / 3
+  #margin_inside: $vertical_rhythm / 4
+  margin_outside: 0
+lead:
+  font_size: $base_font_size_large
+  line_height: 1.4
+abstract:
+  font_color: 5c6266
+  font_size: $lead_font_size
+  line_height: $lead_line_height
+  font_style: italic
+  first_line_font_style: bold
+  title:
+    align: center
+    font_color: $heading_font_color
+    font_family: $heading_font_family
+    font_size: $heading_h4_font_size
+    font_style: $heading_font_style
+admonition:
+  column_rule_color: $base_border_color
+  column_rule_width: $base_border_width
+  padding: [0, $horizontal_rhythm, 0, $horizontal_rhythm]
+  #icon:
+  #  tip:
+  #    name: fa-lightbulb-o
+  #    stroke_color: 111111
+  #    size: 24
+  label:
+    text_transform: uppercase
+    font_style: bold
+blockquote:
+  font_color: $base_font_color
+  font_size: $base_font_size_large
+  border_color: $base_border_color
+  border_width: 5
+  # FIXME disable negative padding bottom once margin collapsing is implemented
+  padding: [0, $horizontal_rhythm, $block_margin_bottom * -0.75, $horizontal_rhythm + $blockquote_border_width / 2]
+  cite_font_size: $base_font_size_small
+  cite_font_color: 999999
+# code is used for source blocks (perhaps change to source or listing?)
+code:
+  font_color: $base_font_color
+  font_family: $literal_font_family
+  font_size: ceil($base_font_size)
+  padding: $code_font_size
+  line_height: 1.25
+  # line_gap is an experimental property to control how a background color is applied to an inline block element
+  line_gap: 3.8
+  background_color: f5f5f5
+  border_color: cccccc
+  border_radius: $base_border_radius
+  border_width: 0.75
+conum:
+  font_family: M+ 1mn
+  font_color: $literal_font_color
+  font_size: $base_font_size
+  line_height: 4 / 3
+example:
+  border_color: $base_border_color
+  border_radius: $base_border_radius
+  border_width: 0.75
+  background_color: ffffff
+  # FIXME reenable padding bottom once margin collapsing is implemented
+  padding: [$vertical_rhythm, $horizontal_rhythm, 0, $horizontal_rhythm]
+image:
+  align: left
+prose:
+  margin_top: $block_margin_top
+  margin_bottom: $block_margin_bottom
+sidebar:
+  background_color: eeeeee
+  border_color: e1e1e1
+  border_radius: $base_border_radius
+  border_width: $base_border_width
+  # FIXME reenable padding bottom once margin collapsing is implemented
+  padding: [$vertical_rhythm, $vertical_rhythm * 1.25, 0, $vertical_rhythm * 1.25]
+  title:
+    align: center
+    font_color: $heading_font_color
+    font_family: $heading_font_family
+    font_size: $heading_h4_font_size
+    font_style: $heading_font_style
+thematic_break:
+  border_color: $base_border_color
+  border_style: solid
+  border_width: $base_border_width
+  margin_top: $vertical_rhythm * 0.5
+  margin_bottom: $vertical_rhythm * 1.5
+description_list:
+  term_font_style: bold
+  term_spacing: $vertical_rhythm / 4
+  description_indent: $horizontal_rhythm * 1.25
+outline_list:
+  indent: $horizontal_rhythm * 1.5
+  #marker_font_color: 404040
+  # NOTE outline_list_item_spacing applies to list items that do not have complex content
+  item_spacing: $vertical_rhythm / 2
+table:
+  background_color: $page_background_color
+  #head_background_color: <hex value>
+  #head_font_color: $base_font_color
+  head_font_style: bold
+  #body_background_color: <hex value>
+  body_stripe_background_color: f9f9f9
+  foot_background_color: f0f0f0
+  border_color: dddddd
+  border_width: $base_border_width
+  cell_padding: 3
+toc:
+  indent: $horizontal_rhythm
+  line_height: 1.4
+  dot_leader:
+    #content: ". "
+    font_color: a9a9a9
+    #levels: 2 3
+# NOTE in addition to footer, header is also supported
+footer:
+  font_size: $base_font_size_small
+  # NOTE if background_color is set, background and border will span width of page
+  #border_color: dddddd                         # EE
+  #border_width: 0.25                           # EE
+  height: $base_line_height_length * 2.5
+  line_height: 1
+  padding: [$base_line_height_length / 2, 1, 0, 1]
+  vertical_align: top
+  #image_vertical_align: <alignment> or <number>
+  # additional attributes for content:
+  # * {page-count}
+  # * {page-number}
+  # * {document-title}
+  # * {document-subtitle}
+  # * {chapter-title}
+  # * {section-title}
+  # * {section-or-chapter-title}
+  recto:
+    #columns: "<50% =0% >50%"
+    right:
+      #content: '{page-number}'                 # EE
+      #content: '{section-or-chapter-title} | {page-number}'
+      #content: '{document-title} | {page-number}'
+      content: '{document-title}{nbsp}{nbsp}{nbsp} *{page-number}*' # EE
+    #center:
+    #  content: '{page-number}'
+    left:                                       # EE
+      content: '{status}'                       # EE
+  verso:
+    #columns: $footer_recto_columns
+    left:
+      #content: $footer_recto_right_content     # EE
+      #content: '{page-number} | {chapter-title}'
+      content: '*{page-number}* {nbsp}{nbsp}{nbsp}{document-title}' # EE
+    #center:
+    #  content: '{page-number}'
+    right:                                      # EE
+      content: '{status}'                       # EE
+header:                                         # EE
+  font_size: $base_font_size_small              # EE
+  border_color: dddddd                          # EE
+  border_width: 0.25                            # EE
+  height: $base_line_height_length * 2.5        # EE
+  line_height: 1                                # EE
+  padding: [$base_line_height_length / 2, 1, 0, 1] # EE
+  vertical_align: top                           # EE
+  recto:                                        # EE
+    right:                                      # EE
+      content: '{section-or-chapter-title}'     # EE
+  verso:                                        # EE
+    left:                                       # EE
+      content: '{section-or-chapter-title}'     # EE
diff --git a/src/assembly/assembly.xml b/src/assembly/assembly.xml
new file mode 100644
index 0000000..8780af6
--- /dev/null
+++ b/src/assembly/assembly.xml
@@ -0,0 +1,45 @@
+<!--
+
+    Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
+
+    This program and the accompanying materials are made available under the
+    terms of the Eclipse Public License v. 2.0, which is available at
+    http://www.eclipse.org/legal/epl-2.0.
+
+    This Source Code may also be made available under the following Secondary
+    Licenses when the conditions for such availability set forth in the
+    Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+    version 2 with the GNU Classpath Exception, which is available at
+    https://www.gnu.org/software/classpath/license.html.
+
+    SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+
+-->
+
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
+
+    <id>el-sources</id>
+    <formats>
+        <format>zip</format>
+    </formats>
+
+    <fileSets>
+        <fileSet>
+            <directory>${project.basedir}</directory>
+            <outputDirectory>/</outputDirectory>
+            <excludes>
+                <exclude>spec/**</exclude>
+                <exclude>fonts/**</exclude>
+                <exclude>target/**</exclude>
+                <exclude>api/target/**</exclude>
+                <exclude>src/assembly/**</exclude>
+            </excludes>
+        </fileSet>
+        <fileSet>
+            <outputDirectory>/</outputDirectory>
+            <directory>${project.build.directory}/license</directory>
+        </fileSet>
+    </fileSets>
+</assembly>             
diff --git a/src/test/java/org/glassfish/el/test/ConvertTest.java b/src/test/java/org/glassfish/el/test/ConvertTest.java
new file mode 100644
index 0000000..814e924
--- /dev/null
+++ b/src/test/java/org/glassfish/el/test/ConvertTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.el.test;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+import javax.el.*;
+
+/**
+ *
+ * @author kichung
+ */
+public class ConvertTest {
+    ELProcessor elp;
+
+    public ConvertTest() {
+    }
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+    }
+
+    @Before
+    public void setUp() {
+        elp = new ELProcessor();
+    }
+
+    @After
+    public void tearDown() {
+    }
+
+    static public class MyBean {
+        String name;
+        int pint;
+        Integer integer;
+
+        MyBean() {
+
+        }
+        MyBean(String name) {
+            this.name = name;
+        }
+        public String getName() {
+            return this.name;
+        }
+        public void setPint(int v) {
+            this.pint = v;
+        }
+        public int getPint() {
+            return this.pint;
+        }
+
+        public void setInteger(Integer i){
+            this.integer = i;
+        }
+
+        public Integer getInteger() {
+            return this.integer;
+        }
+    }
+    @Test
+    public void testVoid() {
+        MyBean bean = new MyBean();
+        elp.defineBean("bean", bean);
+        // Assig null to int is 0;
+        Object obj = elp.eval("bean.pint = null");
+        assertEquals(obj, null);
+        assertEquals(bean.getPint(), 0);
+
+        // Assig null to Integer is null
+        elp.setValue("bean.integer", null);
+        assertEquals(bean.getInteger(), null);
+    }
+
+    @Test
+    public void testCustom() {
+        elp.getELManager().addELResolver(new TypeConverter() {
+            @Override
+            public Object convertToType(ELContext context, Object obj, Class<?> type) {
+                if (obj instanceof String && type == MyBean.class) {
+                    context.setPropertyResolved(true);
+                    return new MyBean((String) obj);
+                }
+                return null;
+            }
+        });
+        
+        Object val = elp.getValue("'John Doe'", MyBean.class);
+        assertTrue(val instanceof MyBean);
+        assertEquals(((MyBean)val).getName(), "John Doe");
+    }
+}
diff --git a/src/test/java/org/glassfish/el/test/Customer.java b/src/test/java/org/glassfish/el/test/Customer.java
new file mode 100644
index 0000000..533ec01
--- /dev/null
+++ b/src/test/java/org/glassfish/el/test/Customer.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.el.test;
+
+import java.util.List;
+import java.util.ArrayList;
+
+public class Customer {
+    int customerID;
+    String name;
+    String address;
+    String city;
+    String country;
+    String phone;
+    List<Order> orders;
+
+    public Customer(int customerID, String name, String address, String city,
+             String country, String phone) {
+        this.customerID = customerID;
+        this.name = name;
+        this.address = address;
+        this.city = city;
+        this.country = country;
+        this.phone = phone;
+        this.orders = new ArrayList<Order>();
+    }
+
+    public String toString() {
+        return "Customer: " + customerID + ", " + name + ", " + city + ", " +
+                country;
+    }
+
+    public int getCustomerID() { return customerID;}
+    public String getName() { return name;}
+    public String getAddress() { return address; }
+    public String getCity() { return city; }
+    public String getCountry() { return country; }
+    public String getPhone() { return phone; }
+    public List<Order> getOrders() { return orders; }
+}
diff --git a/src/test/java/org/glassfish/el/test/DataBase.java b/src/test/java/org/glassfish/el/test/DataBase.java
new file mode 100644
index 0000000..51daab7
--- /dev/null
+++ b/src/test/java/org/glassfish/el/test/DataBase.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.el.test;
+
+import java.util.List;
+import java.util.ArrayList;
+
+public class DataBase {
+
+    private int curCustomer = 100;
+    private int curProduct = 200;
+    private int curOrder = 10;
+    private boolean inited;
+
+    private List<Customer> customers;
+    private List<Product> products;
+    private List<Order> orders;
+
+    public List<Customer> getCustomers() { return this.customers; }
+    public List<Product> getProducts() { return this.products; }
+    public List<Order> getOrders() { return this.orders; }
+
+    public void init() {
+        if (inited) {
+            return;
+        }
+
+        inited = true;
+        customers = new ArrayList<Customer>();
+        orders = new ArrayList<Order>();
+        products = new ArrayList<Product>();
+        initCustomer();
+        initProduct();
+        initOrder();
+    }        
+
+    void initCustomer() {
+        c("John Doe", "123 Willow Road", "Menlo Park", "USA",
+                    "650-734-2187");
+        c("Mary Lane", "75 State Street", "Atlanta", "USA", "302-145-8765");
+        c("Charlie Yeh", "5 Nathan Road", "Kowlon", "Hong Kong", "11-7565-2323");
+    }
+
+    void initProduct() {
+        p("Eagle", "book", 12.50, 100);  // id: 200
+        p("Coming Home", "dvd", 8.00, 50);  // id: 201
+        p("Greatest Hits", "cd", 6.5, 200);  // id: 202
+        p("History of Golf", "book", 11.0, 30);  // id: 203
+        p("Toy Story", "dvd", 10.00, 1000);  // id: 204
+        p("iSee", "book", 12.50, 150);  // 205
+    }
+
+    void initOrder() {
+        o(100, new Date(2010, 2, 18), 20.80);
+        o(100, new Date(2011, 5, 3), 34.50);
+        o(100, new Date(2011, 8, 2), 210.75);
+        o(101, new Date(2011, 1, 15), 50.23);
+        o(101, new Date(2012, 1, 3), 126.77);
+        o(102, new Date(2011, 4, 15), 101.20);
+    }
+
+    void c(String name, String address, String city,
+                  String country, String phone) {
+        customers.add(new Customer(curCustomer++, name, address,
+                                   city, country, phone));
+    }
+
+    void o(int customerID, Date orderDate, double total) {
+        Order order = new Order(curOrder++, customerID, orderDate, total);
+        this.orders.add(order);
+        findCustomer(customerID).getOrders().add(order);
+    }
+
+    void p(String name, String category, double unitPrice, int unitsInStock) {
+        products.add(new Product(curProduct++, name, category, unitPrice,
+                                 unitsInStock));
+    }
+
+    private Customer findCustomer(int id) {
+        for(Customer customer: customers) {
+            if (customer.customerID == id) {
+                return customer;
+            }
+        }
+        return null;
+    }
+}
+
diff --git a/src/test/java/org/glassfish/el/test/Date.java b/src/test/java/org/glassfish/el/test/Date.java
new file mode 100644
index 0000000..f202fb2
--- /dev/null
+++ b/src/test/java/org/glassfish/el/test/Date.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.el.test;
+
+public class Date {
+    int year, month, date;
+
+    public Date(int year, int month, int date) {
+        this.year = year;
+        this.month = month;
+        this.date = date;
+    }
+
+    public int getYear() { return year; }
+    public int getMonth() { return month; }
+    public int getDate() { return date; }
+
+    public String toString() {
+        return "" + month + "/" + date + "/" + year;
+    }
+}
diff --git a/src/test/java/org/glassfish/el/test/ELProcessorTest.java b/src/test/java/org/glassfish/el/test/ELProcessorTest.java
new file mode 100644
index 0000000..3381e7d
--- /dev/null
+++ b/src/test/java/org/glassfish/el/test/ELProcessorTest.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.el.test;
+
+import org.junit.Test;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import javax.el.ELProcessor;
+import javax.el.ELManager;
+import javax.el.ExpressionFactory;
+import javax.el.MethodExpression;
+import javax.el.ELContext;
+import java.lang.reflect.Method;
+
+public class ELProcessorTest {
+
+    static ELProcessor elp;
+    static ELManager elm;
+    static ExpressionFactory factory;
+    
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        elp = new ELProcessor();
+        elm = elp.getELManager();
+        factory = elm.getExpressionFactory();
+    }
+    
+    @Before
+    public void setUp() {
+    }
+
+    @Test
+    public void testMethExpr() {
+        MethodExpression meth = null;
+        ELContext ctxt = elm.getELContext();
+        try {
+            meth = factory.createMethodExpression(
+                ctxt, "#{str.length}", Object.class, null);
+        } catch (NullPointerException ex){
+            // Do nothing
+        }
+        assertTrue(meth == null);
+        meth = factory.createMethodExpression(
+                ctxt, "#{'abc'.length()}", Object.class, null);
+        Object result = meth.invoke(ctxt, new Object[] {"abcde"});
+        System.out.println("'abc'.length() called, equals " + result);
+        assertEquals(result, new Integer(3));
+    }
+
+    @Test
+    public void testGetValue() {
+        Object result = elp.eval("10 + 1");
+        assertEquals(result.toString(), "11");
+        result = elp.getValue("10 + 2", String.class);
+        assertEquals(result, "12");
+    }
+
+    @Test
+    public void testSetVariable () {
+        elp.setVariable("xx", "100");
+        Object result = elp.getValue("xx + 11", String.class);
+        assertEquals(result, "111");
+        elp.setVariable("xx", null);
+        assertEquals(elp.eval("xx"), null);
+        elp.setVariable("yy", "abc");
+        assertEquals(elp.eval("yy = 123; abc"), 123L);
+        assertEquals(elp.eval("abc = 456; yy"), 456L);
+    }
+
+    @Test
+    public void testConcat() {
+        Object result = elp.eval("'10' + 1");
+        assertEquals(result, 11L);
+        result = elp.eval("10 += '1'");
+        assertEquals(result.toString(), "101");
+    }
+    
+    @Test
+    public void defineFuncTest() {
+        Class c = MyBean.class;
+        Method meth = null;
+        Method meth2 = null;
+        try {
+            meth = c.getMethod("getBar", new Class<?>[] {});
+            meth2 = c.getMethod("getFoo", new Class<?>[] {});
+        } catch (Exception e) {
+            System.out.printf("Exception: ", e);
+        }
+        try {
+            elp.defineFunction("xx", "", meth);
+            Object ret = elp.eval("xx:getBar() == 64");
+            assertTrue((Boolean)ret);
+        } catch (NoSuchMethodException ex) {
+            
+        }
+        
+        boolean caught = false;
+        try {
+            elp.defineFunction("", "", meth2);
+            Object ret = elp.eval("getFoo() == 100");
+            assertTrue((Boolean)ret);
+        } catch (NoSuchMethodException ex) {
+            caught = true;
+        }
+        assertTrue(caught);
+        
+        try {
+            elp.defineFunction("yy", "", "org.glassfish.el.test.ELProcessorTest$MyBean", "getBar");
+            Object ret = elp.eval("yy:getBar() == 64");
+            assertTrue((Boolean)ret);
+        } catch (ClassNotFoundException | NoSuchMethodException ex) {
+            
+        }
+        
+        caught = false;
+        try {
+            elp.defineFunction("yy", "", "org.glassfish.el.test.ELProcessorTest$MyBean", "getFooBar");
+            Object ret = elp.eval("yy:getBar() == 100");
+            assertTrue((Boolean)ret);
+        } catch (ClassNotFoundException | NoSuchMethodException ex) {
+            caught = true;
+        }
+        assertTrue(caught);
+        
+        caught = false;
+        try {
+            elp.defineFunction("yy", "", "testBean", "getFoo");
+            Object ret = elp.eval("yy:getBar() == 100");
+            assertTrue((Boolean)ret);
+        } catch (ClassNotFoundException | NoSuchMethodException ex) {
+            caught = true;
+        }
+        assertTrue(caught);
+    }
+/*
+    @Test
+    public void testBean() {
+        elp.defineBean("xyz", new MyBean());
+        Object result = elp.eval("xyz.foo");
+        assertEquals(result.toString(), "100");
+    }
+*/
+
+    @Test
+    public void testImport() {
+        elm.importClass("org.glassfish.el.test.ELProcessorTest$MyBean");
+        assertTrue((Boolean)elp.eval("ELProcessorTest$MyBean.aaaa == 101"));
+        assertTrue((Boolean)elp.eval("ELProcessorTest$MyBean.getBar() == 64"));
+        elm.importStatic("org.glassfish.el.test.ELProcessorTest$MyBean.aaaa");
+        assertEquals(new Integer(101), elp.eval("aaaa"));
+        elm.importStatic("org.glassfish.el.test.ELProcessorTest$MyBean.getBar");
+        assertEquals(new Integer(64), elp.eval("getBar()"));
+ /*
+        elm.importStatic("a.b.NonExisting.foobar");
+        elp.eval("foobar");
+        elp.eval("ELProcessorTest$MyBean.getFoo()");
+        */
+    }
+
+    static public class MyBean {
+        public static int aaaa = 101;
+        public int getFoo() {
+            return 100;
+        }
+        public int getFoo(int i) {
+            return 200;
+        }
+        public static int getBar() {
+            return 64;
+        }
+    }
+}
+
diff --git a/src/test/java/org/glassfish/el/test/ElasticityTest.java.sav b/src/test/java/org/glassfish/el/test/ElasticityTest.java.sav
new file mode 100644
index 0000000..3da7dce
--- /dev/null
+++ b/src/test/java/org/glassfish/el/test/ElasticityTest.java.sav
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.el.test;
+
+import java.util.*;
+import javax.el.*;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author kichung
+ */
+public class ElasticityTest {
+
+    ELProcessor elp;
+
+    public ElasticityTest() {
+    }
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+    }
+
+    @Before
+    public void setUp() {
+        elp = new ELProcessor();
+    }
+
+    @After
+    public void tearDown() {
+    }
+
+    static public class Data {
+        int s;
+        int d;
+
+        public Data(int s, int d) {
+            this.s = s;
+            this.d = d;
+        }
+
+        public int getS() {
+            return this.s;
+        }
+
+        public int getD() {
+            return this.d;
+        }
+    }
+
+    static public class Metric {
+        int limit;
+        List<Data> list = new ArrayList<Data>();
+        
+        public Metric(int limit) {
+            this.limit = limit;
+        }
+        
+        public int getLimit() {
+            return limit;
+        }
+        
+        public List<Data> getList() {
+            return list;
+        }
+    }
+    Map<String, Metric> clusters = new HashMap<String, Metric>();
+
+    private void init() {
+        Metric m1 = new Metric(10);
+        m1.getList().add(new Data(1, 80));
+        m1.getList().add(new Data(3, 90));
+        m1.getList().add(new Data(4, 100));
+        m1.getList().add(new Data(5, 50));
+        m1.getList().add(new Data(6, 60));
+
+        Metric m2 = new Metric(10);
+        m2.getList().add(new Data(1, 80));
+        m2.getList().add(new Data(3, 82));
+        m2.getList().add(new Data(7, 90));
+        m2.getList().add(new Data(9, 140));
+        m2.getList().add(new Data(15, 80));
+
+        Metric m3 = new Metric(10);
+        m3.getList().add(new Data(4, 100));
+        m3.getList().add(new Data(5, 81));
+        m3.getList().add(new Data(6, 200));
+        m3.getList().add(new Data(20, 80));
+
+        clusters.put("c1", m1);
+        clusters.put("c2", m2);
+        clusters.put("c3", m3);
+
+        elp.defineBean("c", clusters);
+    }
+    @Test
+    public void testElaticity() {
+        init();
+        Object obj;
+        
+        obj = elp.eval(
+            "c.values().select(" +
+                "v->v.list.where(d->d.s>1 && d.s<10)." +
+                          "average(d->d.d)).toList()");
+
+        System.out.println(obj);
+        obj = elp.eval(
+            "c.values().select(v->v.list." +
+                       "where(d->d.s>1 && d.s<10)." +
+                       "average(d->d.d) > 100).toList()");
+        System.out.println(obj);
+        obj = elp.eval(
+            "c.values().select(v->v.list." +
+                       "where(d->d.s>1 && d.s<10)." +
+                       "average(d->d.d) > 100).any()");
+        System.out.println(obj);
+        obj = elp.eval(
+            "c.entrySet().select(s->[s.key, s.value.list." +
+                       "where(d->d.s>1 && d.s<10)." +
+                       "average(d->d.d)]).toList()");
+        System.out.println(obj);
+    }
+}
diff --git a/src/test/java/org/glassfish/el/test/EscapingTest.java b/src/test/java/org/glassfish/el/test/EscapingTest.java
new file mode 100644
index 0000000..4f78bd7
--- /dev/null
+++ b/src/test/java/org/glassfish/el/test/EscapingTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.el.test;
+
+import javax.el.ELManager;
+import javax.el.ELProcessor;
+import javax.el.ValueExpression;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class EscapingTest {
+
+    static ELProcessor elp;
+    static ELManager elm;
+
+    public EscapingTest() {
+    }
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        elp = new ELProcessor();
+        elm = elp.getELManager();
+    }
+
+    @Test
+    public void testEscape01() {
+        assertEquals("$2", evaluateExpression("$${1+1}"));
+        assertEquals("$${1+1}", evaluateExpression("$\\${1+1}"));
+    }
+
+    @Test
+    public void testEscape02() {
+        assertEquals("$2", evaluateExpression("$#{1+1}"));
+        assertEquals("$#{1+1}", evaluateExpression("$\\#{1+1}"));
+    }
+
+    @Test
+    public void testEscape03() {
+        assertEquals("#2", evaluateExpression("##{1+1}"));
+        assertEquals("##{1+1}", evaluateExpression("#\\#{1+1}"));
+    }
+
+    @Test
+    public void testEscape04() {
+        assertEquals("#2", evaluateExpression("#${1+1}"));
+        assertEquals("#${1+1}", evaluateExpression("#\\${1+1}"));
+    }
+
+    private String evaluateExpression(String expr) {
+        ValueExpression v = ELManager.getExpressionFactory().createValueExpression(
+                elm.getELContext(), expr, String.class);
+        return (String) v.getValue(elm.getELContext());
+    }
+}
diff --git a/src/test/java/org/glassfish/el/test/EvalListenerTest.java b/src/test/java/org/glassfish/el/test/EvalListenerTest.java
new file mode 100644
index 0000000..410bde2
--- /dev/null
+++ b/src/test/java/org/glassfish/el/test/EvalListenerTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.el.test;
+
+import java.util.ArrayList;
+import javax.el.ELManager;
+import javax.el.ELContext;
+import javax.el.ELProcessor;
+import javax.el.EvaluationListener;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author kichung
+ */
+public class EvalListenerTest {
+
+    public EvalListenerTest() {
+    }
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+    }
+
+    @Before
+    public void setUp() {
+    }
+
+    @After
+    public void tearDown() {
+    }
+
+    @Test
+    public void testEvalListener() {
+        ELProcessor elp = new ELProcessor();
+        ELManager elm = elp.getELManager();
+        final ArrayList<String> msgs = new ArrayList<String>();
+        elm.addEvaluationListener(new EvaluationListener() {
+            @Override
+            public void beforeEvaluation(ELContext ctxt, String expr) {
+                System.out.println("Before: " + expr);
+                msgs.add("Before: " + expr);
+            }
+            @Override
+            public void afterEvaluation(ELContext ctxt, String expr) {
+                System.out.println("After: " + expr);
+                msgs.add("After: " + expr);
+            }
+        });
+        elp.eval("100 + 10");
+        elp.eval("x = 5; x*101");
+        String[] expected = {"Before: ${100 + 10}",
+            "After: ${100 + 10}",
+            "Before: ${x = 5; x*101}",
+            "After: ${x = 5; x*101}" };
+        for (int i = 0; i < expected.length; i++) {
+            assertEquals(expected[i], msgs.get(i));
+        }
+    }
+
+    @Test
+    public void testResListener() {
+        ELProcessor elp = new ELProcessor();
+        ELManager elm = elp.getELManager();
+        final ArrayList<String> msgs = new ArrayList<String>();
+        elm.addEvaluationListener(new EvaluationListener() {
+            @Override
+            public void propertyResolved(ELContext ctxt, Object b, Object p) {
+                System.out.println("Resolved: " + b + " " + p);
+                msgs.add("Resolved: " + b + " " + p);
+            }
+        });
+        elp.eval("x = 10");
+        elp.eval("[1,2,3][2]");
+        elp.eval("'abcd'.length()");
+        elp.eval("'xyz'.class");
+        String[] expected = {
+            "Resolved: null x",
+            "Resolved: [1, 2, 3] 2",
+            "Resolved: abcd length",
+            "Resolved: xyz class"
+        };
+        for (int i = 0; i < expected.length; i++) {
+            assertEquals(expected[i], msgs.get(i));
+        }
+    }
+}
diff --git a/src/test/java/org/glassfish/el/test/LambdaTest.java b/src/test/java/org/glassfish/el/test/LambdaTest.java
new file mode 100644
index 0000000..2734e0c
--- /dev/null
+++ b/src/test/java/org/glassfish/el/test/LambdaTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.el.test;
+
+import org.junit.Test;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import javax.el.ELProcessor;
+
+public class LambdaTest {
+    
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+    }
+    
+    @Before
+    public void setUp() {    
+    } 
+    
+    void testExpr(ELProcessor elp, String testname, String expr, Long expected) {
+        System.out.println("=== Test Lambda Expression:" + testname + " ===");
+        System.out.println(" ** " + expr);
+        Object result = elp.eval(expr);
+        System.out.println("    returns " + result);
+        assertEquals(expected, result);
+    }
+
+    @Test
+    public void testImmediate() {
+        ELProcessor elp = new ELProcessor();
+        testExpr(elp, "immediate", "(x->x+1)(10)", 11L);
+        testExpr(elp, "immediate0", "(()->1001)()", 1001L);
+        testExpr(elp, "immediate1", "((x,y)->x+y)(null, null)", 0L);
+        testExpr(elp, "immediate 2", "(((x,y)->x+y)(3,4))", 7L);
+        testExpr(elp, "immediate 3", "(x->(y=x)+1)(10) + y", 21L);
+    }
+
+    @Test
+    public void testAssignInvoke() {
+        ELProcessor elp = new ELProcessor();
+        testExpr(elp, "assign", "func = x->x+1; func(10)", 11L);
+        testExpr(elp, "assign 2", "func = (x,y)->x+y; func(3,4)", 7L);
+    }
+
+    @Test
+    public void testConditional() {
+        ELProcessor elp = new ELProcessor();
+        elp.eval("cond = true");
+        testExpr(elp, "conditional", "(x->cond? x+1: x+2)(10)", 11L);
+        elp.eval("cond = false");
+        testExpr(elp, "conditional 2",
+                 "func = cond? (x->x+1): (x->x+2); func(10)", 12L);
+    }
+
+    @Test
+    public void testFact() {
+        ELProcessor elp = new ELProcessor();        
+        testExpr(elp, "factorial", "fact = n->n==0? 1: n*fact(n-1); fact(5)", 120L);
+        testExpr(elp, "fibonacci", "f = n->n==0? 0: n==1? 1: f(n-1)+f(n-2); f(10)", 55L);
+    }
+
+    @Test
+    public void testVar() {
+        ELProcessor elp = new ELProcessor();
+        elp.setVariable("v", "x->x+1");
+        testExpr(elp, "assignment to variable", "v(10)", 11L);
+    }
+
+    @Test
+    public void testLambda() {
+        ELProcessor elp = new ELProcessor();
+        testExpr(elp, "Lambda Lambda", "f = ()->y->y+1; f()(100)", 101L);
+        testExpr(elp, "Lambda Lambda 2", "f = (x)->(tem=x; y->tem+y); f(1)(100)", 101L);
+        testExpr(elp, "Lambda Lambda 3", "(()->y->y+1)()(100)", 101L);
+        testExpr(elp, "Lambda Lambda 4", "(x->(y->x+y)(1))(100)", 101L);
+        testExpr(elp, "Lambda Lambda 5", "(x->(y->x+y))(1)(100)", 101L);
+        testExpr(elp, "Lambda Lambda 6"
+                , "(x->y->x(0)+y)(x->x+1)(100)", 101L);
+    }
+}
diff --git a/src/test/java/org/glassfish/el/test/OperatorTest.java b/src/test/java/org/glassfish/el/test/OperatorTest.java
new file mode 100644
index 0000000..a9d512f
--- /dev/null
+++ b/src/test/java/org/glassfish/el/test/OperatorTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.el.test;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+import javax.el.ELProcessor;
+import javax.el.ELManager;
+import javax.el.ExpressionFactory;
+import javax.el.ValueExpression;
+import javax.el.MethodExpression;
+
+/**
+ *
+ * @author Kin-man
+ */
+public class OperatorTest {
+    
+    static ELProcessor elp;
+
+    public OperatorTest() {
+    }
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        elp = new ELProcessor();
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+    }
+    
+    @Before
+    public void setUp() {
+    }
+    
+    void testExpr(String testname, String expr, Long expected) {
+        System.out.println("=== Test " + testname + " ===");
+        System.out.println(" ** " + expr);
+        Object result = elp.eval(expr);
+        System.out.println("    returns " + result);
+        assertEquals(expected, result);
+    }
+    
+    void testExpr(String testname, String expr, String expected) {
+        System.out.println("=== Test " + testname + " ===");
+        System.out.println(" ** " + expr);
+        Object result = elp.eval(expr);
+        System.out.println("    returns " + result);
+        assertEquals(expected, result);
+    }
+    
+    @Test
+    public void testConcat() {
+        testExpr("concat", "a = null; b = null; a + b", 0L);
+        testExpr("add", "10 + 11", 21L);
+        testExpr("concat", "'10' + 11", 21L);
+        testExpr("concat 2", "11 + '10'", 21L);
+        testExpr("concat 3", "100 += 10 ", "10010");
+        testExpr("concat 4", "'100' += 10", "10010");
+        testExpr("concat 5", "'100' + 10 + 1", 111L);
+        testExpr("concat 6", "'100' += 10 + 1", "10011");
+    }
+    
+    @Test
+    public void testAssign() {
+        elp.eval("vv = 10");
+        testExpr("assign", "vv+1", 11L);
+        elp.eval("vv = 100");
+        testExpr("assign 2", "vv", 100L);
+        testExpr("assign 3", "x = vv = vv+1; x + vv", 202L);
+        elp.eval("map = {'one':100, 'two':200}");
+        testExpr("assign 4", "map.two = 201; map.two", 201L);
+        testExpr("assign string", "x='string'; x += 1", "string1");
+    }
+    
+    @Test
+    public void testSemi() {
+        testExpr("semi", "10; 20", 20L);
+        testExpr("semi0", "10; 20; 30", 30L);
+        elp.eval("x = 10; 20");
+        testExpr("semi 2", "x", 10L);
+        testExpr("semi 3", "(x = 10; 20) + (x ; x+1)", 31L);
+        testExpr("semi 4", "(x = 10; y) = 11; x + y", 21L);
+    }
+    @Test
+    public void testMisc() {
+        testExpr("quote", "\"'\"", "'");
+        testExpr("quote", "'\"'", "\"");
+        ELManager elm = elp.getELManager();
+        ValueExpression v = elm.getExpressionFactory().createValueExpression(
+                elm.getELContext(), "#${1+1}", Object.class);
+        Object ret = v.getValue(elm.getELContext());
+        assertEquals(ret, "#2");
+        
+        elp.setVariable("debug", "true");
+        ret = elp.eval("debug == true");
+//        elp.eval("[1,2][true]"); // throws IllegalArgumentExpression
+/*        
+        elp.defineBean("date", new Date(2013, 1,2));
+        elp.eval("date.getYear()");
+        
+        elp.defineBean("href", null);
+        testExpr("space", "(empty href)?'#':href", "#");
+        MethodExpression m = elm.getExpressionFactory().createMethodExpression(
+                elm.getELContext(), "${name}", Object.class, new Class[] {});
+        m.invoke(elm.getELContext(), null);
+        */
+    }
+}
diff --git a/src/test/java/org/glassfish/el/test/Order.java b/src/test/java/org/glassfish/el/test/Order.java
new file mode 100644
index 0000000..6ebfeaf
--- /dev/null
+++ b/src/test/java/org/glassfish/el/test/Order.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.el.test;
+
+public class Order {
+
+    int orderID; 
+    int customerID;
+    Date orderDate;
+    double total;
+
+    public Order(int orderID, int customerID, Date orderDate, double total) {
+        this.orderID = orderID;
+        this.customerID = customerID;
+        this.orderDate = orderDate;
+        this.total = total;
+    }
+
+    public String toString() {
+        return "Order: " + orderID + ", " + customerID +
+            ", " + orderDate.toString() + ", " + total;
+    }
+
+    public int getOrderID() { return orderID; }
+    public int getCustomerID() { return customerID; }
+    public Date getOrderDate() { return orderDate; }
+    public double getTotal() { return total; }
+}
diff --git a/src/test/java/org/glassfish/el/test/OverloadedMethodTest.java b/src/test/java/org/glassfish/el/test/OverloadedMethodTest.java
new file mode 100644
index 0000000..c0a3aa4
--- /dev/null
+++ b/src/test/java/org/glassfish/el/test/OverloadedMethodTest.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.el.test;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+import javax.el.*;
+
+/**
+ *
+ * @author Dongbin Nie
+ */
+public class OverloadedMethodTest {
+
+    ELProcessor elp;
+    ExpressionFactory exprFactory;
+    ELContext elContext;
+
+    @Before
+    public void setUp() {
+        elp = new ELProcessor();
+        exprFactory = elp.getELManager().getExpressionFactory();
+        elContext = elp.getELManager().getELContext();
+        
+        elp.defineBean("foo", new MyBean());
+        
+        elp.defineBean("i1", new I1Impl());
+        elp.defineBean("i2", new I2Impl());
+        elp.defineBean("i12", new I1AndI2Impl());
+        elp.defineBean("i12s", new I1AndI2ImplSub());
+        
+    }
+
+    @After
+    public void tearDown() {
+    }
+    
+    @Test
+    public void testMethodWithNoArg() {
+        assertEquals("methodWithNoArg", elp.eval("foo.methodWithNoArg()"));
+    }
+    
+    @Test
+    public void testMethodNotExisted() {
+        try {
+            elp.eval("foo.methodNotExisted()");
+            fail("testNoExistedMethod Failed");
+        } catch (MethodNotFoundException e) {
+        }
+    }
+    
+    @Test
+    public void testMethodWithSingleArg() {
+        assertEquals("I1", elp.eval("foo.methodWithSingleArg(i1)"));
+        assertEquals("I2Impl", elp.eval("foo.methodWithSingleArg(i2)"));
+        assertEquals("I1AndI2Impl", elp.eval("foo.methodWithSingleArg(i12)"));
+    }
+    
+    @Test
+    public void testMethodWithDoubleArgs() {
+        assertEquals("I1Impl, I2", elp.eval("foo.methodWithDoubleArgs(i1, i2)"));
+        assertEquals("I1, I2", elp.eval("foo.methodWithDoubleArgs(i12, i2)"));
+        assertEquals("I1AndI2Impl, I1AndI2Impl", elp.eval("foo.methodWithDoubleArgs(i12, i12)"));
+        assertEquals("I1AndI2Impl, I1AndI2Impl", elp.eval("foo.methodWithDoubleArgs(i12s, i12)"));
+        assertEquals("I1AndI2Impl, I1AndI2Impl", elp.eval("foo.methodWithDoubleArgs(i12s, i12s)"));
+    }
+    
+    @Test
+    public void testMethodWithAmbiguousArgs() {
+        assertEquals("I1AndI2Impl, I2", elp.eval("foo.methodWithAmbiguousArgs(i12, i2)"));
+        assertEquals("I1, I1AndI2Impl", elp.eval("foo.methodWithAmbiguousArgs(i1, i12)"));
+        try {
+            elp.eval("foo.methodWithAmbiguousArgs(i12, i12)");
+            fail("testMethodWithAmbiguousArgs Failed");
+        } catch (MethodNotFoundException e) {
+        }
+    }
+    
+    @Test
+    public void testMethodWithCoercibleArgs() {
+        assertEquals("String, String", elp.eval("foo.methodWithCoercibleArgs('foo', 'bar')"));
+        assertEquals("String, String", elp.eval("foo.methodWithCoercibleArgs(i1, i12)"));
+        
+        assertEquals("String, String", elp.eval("foo.methodWithCoercibleArgs2(i1, 12345678)"));
+        assertEquals("Integer, Integer", elp.eval("foo.methodWithCoercibleArgs2(12345678, 12345678)"));
+    }
+    
+    @Test
+    public void testMethodWithVarArgs() {
+        assertEquals("I1, I1...", elp.eval("foo.methodWithVarArgs(i1)"));
+        assertEquals("I1, I1...", elp.eval("foo.methodWithVarArgs(i1, i1)"));
+        assertEquals("I1, I1...", elp.eval("foo.methodWithVarArgs(i12, i1, i12)"));
+        
+        assertEquals("I1, I1AndI2Impl...", elp.eval("foo.methodWithVarArgs2(i1)"));
+        assertEquals("I1, I1AndI2Impl...", elp.eval("foo.methodWithVarArgs2(i12)"));
+        assertEquals("I1, I1...", elp.eval("foo.methodWithVarArgs2(i1, i1)"));
+        assertEquals("I1, I1AndI2Impl...", elp.eval("foo.methodWithVarArgs2(i1, i12)"));
+    }
+    
+    @Test
+    public void testExactVarArgs() {
+        String[] args = {"foo", "bar", "hello"};
+        elp.defineBean("args", args);
+        assertEquals("foo,bar,hello,", elp.eval("foo.methodWithExactVarArgs('foo', 'bar', 'hello')"));
+        assertEquals("foo,foo,bar,hello,", elp.eval("foo.methodWithExactVarArgs('foo', args)"));
+    }
+    
+    @Test
+    public void testMethodInStdout() {
+        elp.defineBean("out", System.out);
+        elp.eval("out.println('hello!')");
+        elp.eval("out.println(12345678)");
+    }
+    
+    /**
+     * JSF may invoke MethodExpression which has parameter declared,
+     * but pass in no arguments (not null). 
+     */
+    @Test
+    public void testMethodExprInvokingWithoutArgs() {
+        MethodExpression methodExpr = exprFactory.createMethodExpression(
+                elContext, 
+                "${foo.methodForMethodExpr}", 
+                String.class, 
+                new Class<?>[]{String.class});
+        
+        assertNull(methodExpr.invoke(elContext, new Object[0]));
+    }
+    
+    @Test
+    public void testMethodExprInvoking() {
+        MethodExpression methodExpr = exprFactory.createMethodExpression(
+                elContext, 
+                "${foo.methodForMethodExpr2}", 
+                String.class, 
+                new Class<?>[]{Runnable.class});
+        assertEquals("Runnable", methodExpr.invoke(elContext, new Object[]{Thread.currentThread()}));
+        try {
+            methodExpr.invoke(elContext, new Object[]{"foo"});
+            fail("testMethodExprInvoking Failed");
+        } catch (ELException e) {
+            System.out.println("The following is an expected exception:");
+            e.printStackTrace(System.out);
+        }
+    }
+    
+    
+    public static interface I1 {
+    
+    }
+    
+    public static interface I2 {
+    
+    }
+    
+    public static class I1Impl implements I1 {
+    
+    }
+    
+    public static class I2Impl implements I2 {
+    
+    }
+    
+    public static class I1AndI2Impl implements I1, I2 {
+    
+    }
+    
+    public static class I1AndI2ImplSub extends I1AndI2Impl {
+    
+    }
+
+    static public class MyBean {
+
+        public String methodWithNoArg() {
+            return "methodWithNoArg";
+        }
+
+        public String methodWithSingleArg(I1 i1) {
+            return "I1";
+        }
+
+        public String methodWithSingleArg(I2 i2) {
+            return "I2";
+        }
+
+        public String methodWithSingleArg(I2Impl i2) {
+            return "I2Impl";
+        }
+
+        public String methodWithSingleArg(I1AndI2Impl i1) {
+            return "I1AndI2Impl";
+        }
+
+        public String methodWithDoubleArgs(I1 i1, I2 i2) {
+            return "I1, I2";
+        }
+
+        public String methodWithDoubleArgs(I1Impl i1, I2 i2) {
+            return "I1Impl, I2";
+        }
+
+        public String methodWithDoubleArgs(I1AndI2Impl i1, I1AndI2Impl i2) {
+            return "I1AndI2Impl, I1AndI2Impl";
+        }
+
+        public String methodWithAmbiguousArgs(I1AndI2Impl i1, I2 i2) {
+            return "I1AndI2Impl, I2";
+        }
+
+        public String methodWithAmbiguousArgs(I1 i1, I1AndI2Impl i2) {
+            return "I1, I1AndI2Impl";
+        }
+
+        public String methodWithCoercibleArgs(String s1, String s2) {
+            return "String, String";
+        }
+
+        public String methodWithCoercibleArgs2(String s1, String s2) {
+            return "String, String";
+        }
+
+        public String methodWithCoercibleArgs2(Integer s1, Integer s2) {
+            return "Integer, Integer";
+        }
+
+        public String methodWithVarArgs(I1 i1, I1... i2) {
+            return "I1, I1...";
+        }
+
+        public String methodWithVarArgs2(I1 i1, I1... i2) {
+            return "I1, I1...";
+        }
+
+        public String methodWithVarArgs2(I1 i1, I1AndI2Impl... i2) {
+            return "I1, I1AndI2Impl...";
+        }
+        
+        public String methodWithExactVarArgs(String arg1, String... args) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(arg1).append(",");
+            for (String arg : args) {
+                sb.append(arg).append(",");
+            }
+            return sb.toString();
+        }
+        
+        public String methodForMethodExpr(String arg1) {
+            return arg1;
+        }
+        
+        public String methodForMethodExpr2(Runnable r) {
+            return "Runnable";
+        }
+        
+        public String methodForMethodExpr2(String s) {
+            return "String";
+        }
+
+    }
+    
+}
diff --git a/src/test/java/org/glassfish/el/test/Product.java b/src/test/java/org/glassfish/el/test/Product.java
new file mode 100644
index 0000000..72d9cd5
--- /dev/null
+++ b/src/test/java/org/glassfish/el/test/Product.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.el.test;
+
+public class Product {
+
+    public int productID; 
+    public String name;
+    public String category;
+    public double unitPrice;
+    public int unitsInStock;
+
+    Product (int productID, String name, String category,
+             double unitPrice, int unitsInStock) {
+
+        this.productID = productID;
+        this.name = name;
+        this.category = category;
+        this.unitPrice = unitPrice;
+        this.unitsInStock = unitsInStock;
+    }
+
+    public String toString() {
+        return "Product: " + productID + ", " + name + ", " +
+            category + ", " + unitPrice + ", " + unitsInStock;
+    }
+
+    public int getProductID() { return productID; }
+    public String getName() { return name; }
+    public String getCategory() { return category; }
+    public double getUnitPrice() { return unitPrice; }
+    public int getUnitsInStock() { return unitsInStock; }
+
+}
diff --git a/src/test/java/org/glassfish/el/test/StaticRefTest.java b/src/test/java/org/glassfish/el/test/StaticRefTest.java
new file mode 100644
index 0000000..4c8df7b
--- /dev/null
+++ b/src/test/java/org/glassfish/el/test/StaticRefTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.el.test;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+import javax.el.*;
+
+/**
+ *
+ * @author kichung
+ */
+public class StaticRefTest {
+
+    ELProcessor elp;
+
+    public StaticRefTest() {
+    }
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+    }
+
+    @AfterClass
+    public static void tearDownClass() throws Exception {
+    }
+
+    @Before
+    public void setUp() {
+        elp = new ELProcessor();
+    }
+
+    @After
+    public void tearDown() {
+    }
+
+    @Test
+    public void testStaticRef() {
+        // Pre imported java.lang classes
+//        assertTrue((Boolean)elp.eval("T(java.lang.Boolean).TRUE"));
+//        assertTrue((Boolean)elp.eval("T(Boolean).TRUE"));
+        assertTrue((Boolean)elp.eval("Boolean.TRUE"));
+        assertTrue((Boolean)elp.eval("Boolean.TRUE"));  // test caching Boolean
+    }
+
+/*
+    @Test
+    public void testClass() {
+        assertEquals(String.class, elp.eval("String.class"));
+    }
+*/
+
+    @Test
+    public void testConstructor() {
+//        assertEquals(new Integer(1001), elp.eval("T(Integer)(1001)"));
+        assertEquals(new Integer(1001), elp.eval("Integer(1001)"));
+    }
+
+    @Test
+    public void testStaticMethod() {
+        assertEquals(4, elp.eval("Integer.numberOfTrailingZeros(16)"));
+    }
+}
diff --git a/src/test/java/org/glassfish/el/test/StreamTest.java b/src/test/java/org/glassfish/el/test/StreamTest.java
new file mode 100644
index 0000000..c37c71e
--- /dev/null
+++ b/src/test/java/org/glassfish/el/test/StreamTest.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+package org.glassfish.el.test;
+
+import java.lang.reflect.Array;
+import java.util.List;
+import java.util.Map;
+import java.util.Iterator;
+
+import org.junit.Test;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import javax.el.ELProcessor;
+import javax.el.ELException;
+
+public class StreamTest {
+
+    static ELProcessor elp;
+    static DataBase database = null;
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        elp = new ELProcessor();
+        database = new DataBase();
+        database.init();
+        elp.defineBean("customers", database.getCustomers());
+        elp.defineBean("products", database.getProducts());
+        elp.defineBean("orders", database.getOrders());
+    }
+
+    @Before
+    public void setup() {
+    }
+
+    void p(String msg) {
+        System.out.println(msg);
+    }
+    /**
+     * Test a collection query that returns an array, list or Iterable.
+     * @param name of the test
+     * @param query The EL query string
+     * @param expected The expected result of the array, list or Iterable.
+     *     The array element should equal the elements in the array, list or
+     *     Iterable, when enumerated.
+     */
+    void testStream(String name, String query, String[] expected) {
+        p("=== Testing " + name + " ===");
+        p(query);
+        Object ret = elp.eval(query);
+        p(" = returns =");
+
+        if (ret.getClass().isArray()) {
+            int size = Array.getLength(ret);
+            assertTrue(size == expected.length);
+            for (int i = 0; i < size; i++) {
+                Object item = Array.get(ret, i);
+                p(" " + item.toString());
+                assertEquals(item.toString(), expected[i]);
+            }
+            return;
+        }
+
+        if (ret instanceof List) {
+            List<Object> list = (List<Object>) ret;
+            int i = 0;
+            for (Object item: list) {
+                p(" " + item.toString());
+                assertEquals(item.toString(), expected[i++]); 
+            }
+            assertTrue(i == expected.length);
+            return;
+        }
+
+        if (ret instanceof Iterator) {
+            int i = 0;
+            Iterator<Object> iter = (Iterator<Object>) ret;
+            while (iter.hasNext()) {
+                Object item = iter.next();
+                p(" " + item.toString());
+                assertEquals(item.toString(), expected[i++]);
+            }
+            assertTrue(i == expected.length);
+            return;
+        }
+
+        // unexpected return type
+        assertTrue(false);
+    }
+
+    void testStream(String name, String query, Object expected) {
+        p("=== Testing " + name + " ===");
+        p(query);
+        Object ret = elp.eval(query);
+        p(" = returns " + ret  + "(" + ret.getClass() + ")");
+        assertEquals(ret, expected);
+        p("");
+    }
+
+    static String[] exp0 = {"1", "2", "3", "4", "5", "6"};
+    static String[] exp1 = {"6", "5", "4", "3", "2", "1"};
+    static String[] exp2 = {"q", "z", "yz", "aaa", "abc", "xyz"};
+    static String[] exp3 = {"2", "3", "4"};
+    static String[] exp4 = {"20", "30", "40"};
+
+    @Test
+    public void testFilterMap() {
+        testStream("filter", "[1,2,3,4].stream().filter(i->i > 1).toList()", exp3);
+        testStream("map", "[2,3,4].stream().map(i->i*10).iterator()", exp4);
+        testStream("filtermap", "[1,2,3,4].stream().filter(i->i > 1)\n" +
+                                "                  .map(i->i*10).toArray()", exp4);
+    }
+
+    static String[] exp5 = {
+        "Product: 201, Coming Home, dvd, 8.0, 50",
+        "Product: 200, Eagle, book, 12.5, 100",
+        "Product: 202, Greatest Hits, cd, 6.5, 200",
+        "Product: 203, History of Golf, book, 11.0, 30",
+        "Product: 204, Toy Story, dvd, 10.0, 1000",
+        "Product: 205, iSee, book, 12.5, 150"};
+
+    static String[] exp6 = {
+        "Product: 203, History of Golf, book, 11.0, 30",
+        "Product: 200, Eagle, book, 12.5, 100",
+        "Product: 205, iSee, book, 12.5, 150",
+        "Product: 202, Greatest Hits, cd, 6.5, 200",
+        "Product: 201, Coming Home, dvd, 8.0, 50",
+        "Product: 204, Toy Story, dvd, 10.0, 1000"};
+
+    @Test
+    public void testSorted() {
+        testStream("distinct", "[2, 3, 2, 4, 4].stream().distinct().toList()", exp3);
+        testStream("sorted", "[1, 3, 5, 2, 4, 6].stream().sorted().toList()", exp0);
+        testStream("sorted", "[1, 3, 5, 2, 4, 6].stream().sorted((i,j)->i-j).toList()", exp0);
+        testStream("sorted", "[1, 3, 5, 2, 4, 6].stream().sorted((i,j)->i.compareTo(j)).toList()", exp0);
+        testStream("sorted", "['2', '4', '6', '5', '3', '1'].stream().sorted((s, t)->s.compareTo(t)).toList()", exp0);
+        testStream("sorted", "[1, 3, 5, 2, 4, 6].stream().sorted((i,j)->j.compareTo(i)).toList()", exp1);
+        testStream("sorted", "['xyz', 'yz', 'z', 'abc', 'aaa', 'q'].stream().sorted" +
+                "((s,t)->(s.length()== t.length()? s.compareTo(t): s.length() - t.length())).toList()",
+                exp2);
+        elp.eval("comparing = map->(x,y)->map(x).compareTo(map(y))");
+        testStream("sorted", "products.stream().sorted(" +
+            "(x,y)->x.name.compareTo(y.name)).toList()", exp5);
+        testStream("sorted", "products.stream().sorted(" +
+            "comparing(p->p.name)).toList()", exp5);
+        elp.eval("compose = (m1,m2)->(x,y)->(tx = m1(x).compareTo(m1(y)); "
+                + "tx!=0? tx: (m2(x).compareTo(m2(y))))");
+        testStream("sorted", "products.stream().sorted(" +
+                "compose(p->p.category, p->p.unitPrice)).toList()", exp6);
+    }
+
+    static String exp8[] = {"Eagle", "Coming Home", "Greatest Hits",
+                       "History of Golf", "Toy Story" , "iSee"};
+
+    String exp11[] = {"1","2","3","4"};
+    @Test
+    public void testForEach() {
+        testStream("forEach",
+            "lst = []; products.stream().forEach(p->lst.add(p.name)); lst", exp8);
+        testStream("peek",
+             "lst = []; [1,2,3,4].stream().peek(i->lst.add(i)).toList()", exp11);
+        testStream("peek2", "lst", exp11);
+    }
+
+    static String[] exp7 = {
+        "Order: 10, 100, 2/18/2010, 20.8",
+        "Order: 11, 100, 5/3/2011, 34.5",
+        "Order: 12, 100, 8/2/2011, 210.75",
+        "Order: 13, 101, 1/15/2011, 50.23",
+        "Order: 14, 101, 1/3/2012, 126.77"};
+    
+    static String[] exp9 = {"t","h","e","q","u","i","c","k","b","r","o","w","n","f","o","x"};
+
+    @Test
+    public void testFlapMap() {
+        testStream("flatMap",
+            "customers.stream().filter(c->c.country=='USA')\n" +
+            "                  .flatMap(c->c.orders.stream()).toList()",
+            exp7);
+        testStream("flatMap String",
+             "['the', 'quick', 'brown', 'fox']" +
+             ".stream().flatMap(s->s.toCharArray().stream()).toList()",
+             exp9);
+    }
+
+    static String exp10[] = {"0", "1", "2"};
+
+    @Test
+    public void testSubstream() {
+        testStream("limit", "[0,1,2,3,4,5].stream().limit(3).toList()", exp10);
+        testStream("substream", "[0,1,2,3,4].stream().substream(2).toList()", exp3);
+        testStream("substream", "[0,1,2,3,4,5,6].stream().substream(2,5).toList()", exp3);
+    }
+
+    @Test
+    public void testReduce() {
+        testStream("reduce", "[1,2,3,4,5].stream().reduce(0, (l,r)->l+r)", Long.valueOf(15));
+        testStream("reduce", "[1,2,3,4,5].stream().reduce((l,r)->l+r).get()", Long.valueOf(15));
+        testStream("reduce", "[].stream().reduce((l,r)->l+r).orElse(101)", Long.valueOf(101));
+        testStream("reduce", "[].stream().reduce((l,r)->l+r).orElseGet(()->101)", Long.valueOf(101));
+        testStream("reduce", "c = 0; [1,2,3,4,5,6].stream().reduce(0, (l,r)->(c = c+1; c % 2 == 0? l+r: l-r))", Long.valueOf(3));
+    }
+
+    @Test
+    public void testMatch() {
+        testStream("anyMatch", "[1,2,3,4].stream().anyMatch(e->e == 3)", Boolean.TRUE);
+        testStream("anyMatch", "[1,2,3,4].stream().anyMatch(e->e > 10)", Boolean.FALSE);
+        testStream("allMatch", "[1,2,3,4].stream().allMatch(e->e > 0)", Boolean.TRUE);
+        testStream("allMatch", "[1,2,3,4].stream().allMatch(e->e > 1)", Boolean.FALSE);
+        testStream("noneMatch", "[1,2,3,4].stream().noneMatch(e->e > 1)", Boolean.FALSE);
+        testStream("noneMatch", "[1,2,3,4].stream().noneMatch(e->e > 10)", Boolean.TRUE);
+    }
+
+    @Test
+    public void testToType() {
+        testStream("toArray", "[2,3,4].stream().map(i->i*10).toArray()", exp4);
+        testStream("toList", "[2,3,4].stream().map(i->i*10).toList()", exp4);
+        testStream("Iterator", "[2,3,4].stream().map(i->i*10).iterator()", exp4);
+    }
+
+    @Test
+    public void testFind() {
+        testStream("findFirst", "[101, 100].stream().findFirst().get()", Long.valueOf(101));
+        boolean caught = false;
+        try {
+            elp.eval("[].stream().findFirst().get()");
+        } catch (ELException ex) {
+            caught = true;
+        }
+        assertTrue(caught);
+        testStream("findFirst", "[101, 100].stream().findFirst().isPresent()", Boolean.TRUE);
+        testStream("findFirst", "[].stream().findFirst().isPresent()", Boolean.FALSE);
+    }
+
+    @Test
+    public void testArith() {
+        testStream("sum", "[1,2,3,4,5].stream().sum()", Long.valueOf(15)); 
+        testStream("sum", "[1.4,2,3,4,5.1].stream().sum()", Double.valueOf(15.5)); 
+        testStream("average", "[1,2,3,4,5].stream().average().get()", Double.valueOf(3.0)); 
+        testStream("average", "[1.4,2,3,4,5.1].stream().average().get()", Double.valueOf(3.1)); 
+        testStream("count", "[1,2,3,4,5].stream().count()", Long.valueOf(5));
+    }
+    
+    @Test
+    public void testMinMax() {
+        testStream("min", "[2,3,1,5].stream().min().get()", Long.valueOf(1));
+        testStream("max", "[2,3,1,5].stream().max().get()", Long.valueOf(5));
+        testStream("max", "['xy', 'xyz', 'abc'].stream().max().get()", "xyz");
+        testStream("max", "[2].stream().max((i,j)->i-j).get()", Long.valueOf(2));
+        elp.eval("comparing = map->(x,y)->map(x).compareTo(map(y))");
+        testStream("max", "customers.stream().max((x,y)->x.orders.size()-y.orders.size()).get().name", "John Doe");
+        testStream("max", "customers.stream().max(comparing(c->c.orders.size())).get().name", "John Doe");
+        testStream("min", "[3,2,1].stream().min((i,j)->i-j).get()", Long.valueOf(1));
+        testStream("min", "customers.stream().min((x,y)->x.orders.size()-y.orders.size()).get().name", "Charlie Yeh");
+        elp.eval("comparing = map->(x,y)->map(x).compareTo(map(y))");
+        testStream("min", "customers.stream().min(comparing(c->c.orders.size())).get().name", "Charlie Yeh");
+    }
+    
+    @Test
+    public void testMap() {
+        Object r = elp.eval("v = {'one':1, 'two':2}");
+        System.out.println(" "+ r);
+        r = elp.eval("{1,2,3}");
+        System.out.println(" "+ r);
+    }
+}
diff --git a/uel/.gitkeep b/uel/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/uel/.gitkeep