Initial contribution

Signed-off-by: Yamini K B <yamini.k.b@oracle.com>
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..dcd9d5a
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,47 @@
+# Contributing to Eclipse Project for Expression Language
+
+Thanks for your interest in this project.
+
+## Project description
+
+Expression Language (also referred to as the EL) provides an important mechanism
+for enabling the presentation layer (web pages) to communicate with the
+application logic (managed beans). The EL is used by several JavaEE
+technologies, such as JavaServer Faces technology, JavaServer Pages (JSP)
+technology, and Contexts and Dependency Injection for Java EE (CDI). The EL 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..bf84fa8
--- /dev/null
+++ b/NOTICE.md
@@ -0,0 +1,46 @@
+# Notices for Eclipse Project for Expression Language
+
+This content is produced and maintained by the Eclipse Project for Expression
+Language project.
+
+* Project home: https://projects.eclipse.org/projects/ee4j.el
+
+## Trademarks
+
+Eclipse Project for 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..4259f5d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,9 @@
+# Prerequisites
+
+Oracle JDK 1.7
+
+Apache Maven 3.3.9 or later in order to build and run the tests.
+
+# Licenses
+
+Applicable licenses for this project are documented in LICENSE.md available at the root of this repository.
diff --git a/api/pom.xml b/api/pom.xml
new file mode 100644
index 0000000..df1e03c
--- /dev/null
+++ b/api/pom.xml
@@ -0,0 +1,259 @@
+<?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">
+    <parent>
+      <groupId>org.eclipse.ee4j</groupId>
+      <artifactId>project</artifactId>
+      <version>1.0</version>
+      <relativePath/>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>javax.el</groupId>
+    <artifactId>javax.el-api</artifactId>
+    <packaging>jar</packaging>
+    <version>3.0.1-b07-SNAPSHOT</version>
+    <name>Expression Language 3.0 API</name>
+
+    <properties>
+        <!-- Make sure the two versions are in sync with the maven version -->
+        <spec.version>3.0</spec.version>
+        <bundle.version>3.0.0</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>
+    
+    <url>https://projects.eclipse.org/projects/ee4j.el</url>
+
+    <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>EL 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>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <version>1.4.3</version>
+                <configuration>
+                    <supportedProjectTypes>
+                        <supportedProjectType>jar</supportedProjectType>
+                    </supportedProjectTypes>
+                    <instructions>
+                        <Bundle-SymbolicName>${bundle.symbolicName}</Bundle-SymbolicName>
+                        <Bundle-Description>
+                            Expression Language ${spec.version} API
+                        </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>
+            <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>
+            <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-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>
+                <version>2.8.1</version>
+                <executions>
+                    <execution>
+                        <id>attach-javadocs</id>
+                            <goals>
+                                <goal>jar</goal>
+                            </goals>
+                        <configuration>
+                            <groups>
+                                <group>
+                                    <title>Expresion Language API Documentation</title>
+                                    <packages>javax.el</packages>
+                                </group>
+                            </groups>
+                            <bottom>
+<![CDATA[Copyright &#169; 2013,
+    <a href="http://www.oracle.com">Oracle</a>
+    and/or its affiliates. All Rights Reserved.
+    Use is subject to
+  <a href="{@docRoot}/doc-files/final-spec-license.html" target="_top">license terms</a>.
+]]>
+                            </bottom>
+                        </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-remote-resources-plugin</artifactId>
+                <version>1.2.1</version>
+                <executions>
+                  <execution>
+                    <goals>
+                      <goal>process</goal>
+                    </goals>
+                    <configuration>
+                      <resourceBundles>
+                        <resourceBundle>org.glassfish:legal:1.1</resourceBundle>
+                      </resourceBundles>
+                    </configuration>
+                  </execution>
+                </executions>
+            </plugin>
+    	</plugins>
+        <resources>
+            <resource>
+                <directory>src/main/java</directory>
+                <includes>
+                    <include>**/*.properties</include>
+                </includes>
+            </resource>
+        </resources>
+    </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..11c5c8c
--- /dev/null
+++ b/api/src/main/java/javax/el/ArrayELResolver.java
@@ -0,0 +1,344 @@
+/*
+ * 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.
+ */
+
+package javax.el;
+
+import java.lang.reflect.Array;
+import java.util.List;
+import java.util.Iterator;
+import java.beans.FeatureDescriptor;
+
+/**
+ * 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 JSP 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.
+     */
+    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.
+     */
+    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.
+     */
+    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.
+     */
+    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>.
+     */
+    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>.
+     */
+    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..ea4418a
--- /dev/null
+++ b/api/src/main/java/javax/el/BeanELResolver.java
@@ -0,0 +1,654 @@
+/*
+ * 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.
+ */
+
+package javax.el;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.ref.SoftReference;
+import java.lang.ref.ReferenceQueue;
+import java.beans.FeatureDescriptor;
+import java.beans.BeanInfo;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.beans.IntrospectionException;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashMap;
+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 JSP 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 int CACHE_INIT_SIZE = 1024;
+        private ConcurrentHashMap<Class<?>, BPSoftReference> map =
+            new ConcurrentHashMap<Class<?>, BPSoftReference>(CACHE_INIT_SIZE);
+        private ReferenceQueue<BeanProperties> refQ =
+                        new ReferenceQueue<BeanProperties>();
+
+        // 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<String, BeanProperty>();
+                                                                                
+        public BeanProperties(Class<?> baseClass) {
+            PropertyDescriptor[] descriptors;
+            try {
+                BeanInfo info = Introspector.getBeanInfo(baseClass);
+                descriptors = info.getPropertyDescriptors();
+            } catch (IntrospectionException ie) {
+                throw new ELException(ie);
+            }
+            for (PropertyDescriptor pd: descriptors) {
+                propertyMap.put(pd.getName(),
+                                new BeanProperty(baseClass, pd));
+            }
+        }
+                                                                                
+        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.
+     */
+    public Class<?> getType(ELContext context,
+                         Object base,
+                         Object property) {
+
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base == null || property == null){
+            return null;
+        }
+
+        BeanProperty bp = getBeanProperty(context, base, property);
+        context.setPropertyResolved(true);
+        return bp.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.
+     */
+    public Object getValue(ELContext context,
+                           Object base,
+                           Object property) {
+
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (base == null || property == null){
+            return null;
+        }
+
+        BeanProperty bp = getBeanProperty(context, base, property);
+        Method method = bp.getReadMethod();
+        if (method == null) {
+            throw new PropertyNotFoundException(
+                        ELUtil.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.
+     */
+    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(
+                        ELUtil.getExceptionMessageString(context,
+                            "resolverNotwritable",
+                            new Object[] { base.getClass().getName() }));
+        } 
+
+        BeanProperty bp = getBeanProperty(context, base, property);
+        Method method = bp.getWriteMethod();
+        if (method == null) {
+            throw new PropertyNotWritableException(
+                        ELUtil.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 = ELUtil.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 method 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 EL 2.2
+     */
+
+    public Object invoke(ELContext context,
+                         Object base,
+                         Object method,
+                         Class<?>[] paramTypes,
+                         Object[] params) {
+
+        if (base == null || method == null) {
+            return null;
+        }
+        Method m = ELUtil.findMethod(base.getClass(), method.toString(),
+                                    paramTypes,params, false);
+        for (Object p: params) {
+            // If the parameters is a LambdaExpression, set the ELContext
+            // for its evaluation
+            if (p instanceof javax.el.LambdaExpression) {
+                ((javax.el.LambdaExpression) p).setELContext(context);
+            }
+        }
+        Object ret = ELUtil.invokeMethod(context, m, base, params);
+        context.setPropertyResolved(base, method);
+        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.
+     */
+    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;
+        }
+
+        BeanProperty bp = getBeanProperty(context, base, property);
+        return bp.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:
+     * <dl>
+     *     <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>
+     * </dl>
+     * </p>
+     * 
+     * @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>.
+     */
+    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> list = new ArrayList<FeatureDescriptor>(
+                info.getPropertyDescriptors().length);
+        for (PropertyDescriptor pd: info.getPropertyDescriptors()) {
+            pd.setValue("type", pd.getPropertyType());
+            pd.setValue("resolvableAtDesignTime", Boolean.TRUE);
+            list.add(pd);
+        }
+        return list.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>.
+     */
+    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 bps = properties.get(baseClass);
+        if (bps == null) {
+            bps = new BeanProperties(baseClass);
+            properties.put(baseClass, bps);
+        }
+        BeanProperty bp = bps.getBeanProperty(property);
+        if (bp == null) {
+            throw new PropertyNotFoundException(
+                        ELUtil.getExceptionMessageString(context,
+                            "propertyNotFound",
+                            new Object[] { baseClass.getName(),
+                                           property}));
+        }
+        return bp;
+    }
+}
+
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..2fc92f8
--- /dev/null
+++ b/api/src/main/java/javax/el/BeanNameELResolver.java
@@ -0,0 +1,233 @@
+/*
+ * 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 javax.el;
+
+import java.util.Iterator;
+import java.beans.FeatureDescriptor;
+
+/**
+ * <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.
+ * <blockquote>
+ * <pre>
+ * 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;
+ *    }
+ * });
+ * </pre>
+ * </blockquote>
+ * </p>
+ * @since EL 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>.
+     */
+    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..3541173
--- /dev/null
+++ b/api/src/main/java/javax/el/BeanNameResolver.java
@@ -0,0 +1,83 @@
+/*
+ * 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 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 EL 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..2c46e33
--- /dev/null
+++ b/api/src/main/java/javax/el/CompositeELResolver.java
@@ -0,0 +1,592 @@
+/*
+ * 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.
+ */
+
+package javax.el;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.beans.FeatureDescriptor;
+
+/**
+ * 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 JSP 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>
+     *
+     * <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>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>
+     *
+     * <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 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 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;
+    }
+
+    /**
+     * Attemps 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>
+     *
+     * <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 EL 2.2
+     */
+    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>
+     *
+     * <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.
+     */
+    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>
+     *
+     * <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>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>
+     *
+     * <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>.
+     *
+     * <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 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.
+     */
+    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>
+     *
+     * <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.
+     */
+    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
+     */
+    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.</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 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 EL 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 EL 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;
+        }
+
+        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();
+        }
+
+        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();
+        }
+
+        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..e8f5d93
--- /dev/null
+++ b/api/src/main/java/javax/el/ELClass.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2013, 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 javax.el;
+
+/**
+ * <p>A runtime representation of a Class in the EL expressions.
+ * It encapsulates the java.lang.Class instance.</p>
+ * 
+ * <p>This class is used only in {@link StaticFieldELResolver} and will
+ * probably only be of interest to EL implementors, and not EL users.
+ *
+ * @since EL 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..7bc26da
--- /dev/null
+++ b/api/src/main/java/javax/el/ELContext.java
@@ -0,0 +1,446 @@
+/*
+ * 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.
+ */
+
+package javax.el;
+
+import java.util.Map;
+import java.util.Stack;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Locale;
+
+/**
+ * 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 EL Functions.  This is used only in parsing.</li>
+ *   <li>a reference to {@link VariableMapper} that will be used
+ *       to resolve EL 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>
+ *
+ * <p>The collection of context objects is necessary because each 
+ * <code>ELResolver</code> may need access to a different context object.
+ * For example, JSP and Faces resolvers need access to a 
+ * {@link javax.servlet.jsp.JspContext} and a
+ * {@link javax.faces.context.FacesContext}, respectively.</p>
+ *
+ * <p>When used in a web container, the creation of
+ * <code>ELContext</code> objects is controlled through 
+ * the underlying technology.  For example, in JSP 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>
+ *
+ * <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.</p>
+ *
+ * @see ELContextListener
+ * @see ELContextEvent
+ * @see ELResolver
+ * @see FunctionMapper
+ * @see VariableMapper
+ * @see ImportHandler
+ * @see LambdaExpression
+ * @see StandardELContext
+ * @see javax.servlet.jsp.JspContext
+ * @since EL 2.1 and EL 3.0
+ */
+public abstract class ELContext {
+
+    /**
+     * 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.</p>
+     *
+     * @see CompositeELResolver
+     * @param base The base object
+     * @param property The property object
+     *
+     * @since EL 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.</p>
+     *
+     * @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 EL 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
+     * EL functions.
+     */
+    public abstract FunctionMapper getFunctionMapper();
+    
+    /**
+     * Holds value of property locale.
+     */
+    private Locale locale;
+    
+    /**
+     * 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 this.locale;
+    }
+
+    /**
+     * Sets the <code>Locale</code> for this instance.  This method may be 
+     * called by the party creating the instance, such as JavaServer
+     * Faces or JSP, to enable the EL 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>.
+     */
+    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
+     * EL variables.
+     */
+    public abstract VariableMapper getVariableMapper();
+
+    /**
+     * Registers an evaluation listener to the ELContext.
+     *
+     * @param listener The listener to be added.
+     *
+     * @since EL 3.0
+     */
+    public void addEvaluationListener(EvaluationListener listener) {
+        if (listeners == null) {
+            listeners = new ArrayList<EvaluationListener>();
+        }
+        listeners.add(listener);
+    }
+
+    /**
+     * Returns the list of registered evaluation listeners.
+     * @return The list of registered evaluation listeners.
+     *
+     * @since EL 3.0
+     */
+    public List<EvaluationListener> getEvaluationListeners() {
+        return listeners;
+    }
+
+    /**
+     * Notifies the listeners before an EL expression is evaluated
+     * @param expr The EL 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 EL expression is evaluated
+     * @param expr The EL 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 EL 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 EL 3.0
+     */
+    public void enterLambdaScope(Map<String,Object> args) {
+        if (lambdaArgs == null) {
+            lambdaArgs = new Stack<Map<String,Object>>();
+        }
+        lambdaArgs.push(args);
+    }
+
+    /**
+     * Exits the Lambda expression evaluation. The Lambda argument map that
+     * was previously installed is removed.
+     * @since EL 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.</p>
+     *
+     * @param obj The object to convert.
+     * @param targetType The target type for the conversion.
+     * @throws ELException thrown if errors occur.
+     *
+     * @since EL 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);
+    }
+
+    private boolean resolved;
+    private HashMap<Class<?>, Object> map = new HashMap<Class<?>, Object>();
+    private transient List<EvaluationListener> listeners = null;
+    private Stack<Map<String,Object>> lambdaArgs;
+    private ImportHandler importHandler;
+}
+
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..2af7b92
--- /dev/null
+++ b/api/src/main/java/javax/el/ELContextEvent.java
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package javax.el;
+
+/**
+ * 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 JSP 2.1
+ */
+public class ELContextEvent extends java.util.EventObject {
+
+    /**
+     * 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..b4df47b
--- /dev/null
+++ b/api/src/main/java/javax/el/ELContextListener.java
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package javax.el;
+
+/**
+ * The listener interface for receiving notification when an
+ * {@link ELContext} is created.
+ *
+ * @see ELContext
+ * @see ELContextEvent
+ * @since JSP 2.1
+ */
+public interface ELContextListener extends java.util.EventListener {
+
+    /**
+     * Invoked when a new <code>ELContext</code> has been created.
+     *
+     * @param ece the notification event.
+     */
+    public 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..3f39601
--- /dev/null
+++ b/api/src/main/java/javax/el/ELException.java
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package javax.el;
+
+/**
+ * Represents any of the exception conditions that can arise during
+ * expression evaluation.
+ *
+ * @since JSP 2.1
+ */
+public class ELException extends RuntimeException {
+
+    //-------------------------------------
+    /**
+     * 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..5d06e22
--- /dev/null
+++ b/api/src/main/java/javax/el/ELManager.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2013, 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 javax.el;
+
+import java.lang.reflect.Method;
+
+/**
+ * <p>Manages EL parsing and evaluation environment.  The ELManager maintains an
+ * instance of ExpressionFactory and StandardELContext, for
+ * parsing and evaluating EL expressions.</p>
+ *
+ * @since EL 3.0
+ */
+public class ELManager {
+
+    private StandardELContext elContext;
+
+    /**
+     * Return the ExpressionFactory instance used for EL evaluations.
+     * @return The ExpressionFactory
+     */
+    public static ExpressionFactory getExpressionFactory() {
+        return ELUtil.getExpressionFactory();
+    }
+
+    /**
+     * Return the ELContext used for parsing and evaluating EL expressions.
+     * If there is currently no ELContext, a default instance of
+     * StandardELContext is returned.
+     *
+     * @return The ELContext used for parsing and evaluating EL expressions..
+     */
+    public StandardELContext getELContext() {
+        if (elContext == null) {
+            elContext = new StandardELContext(getExpressionFactory());
+        }
+        return elContext;
+    }
+
+    /**
+     * Set the ELContext used for parsing and evaluating EL 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 prev = elContext;
+        elContext = new StandardELContext(context);
+        return prev;
+    }
+
+    /**
+     * 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 bnr The BeanNameResolver to be registered.
+     */
+    public void addBeanNameResolver(BeanNameResolver bnr) {
+        getELContext().addELResolver(new BeanNameELResolver(bnr));
+    }
+
+    /**
+     * 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 elr The ELResolver to be added to the list of ELResolvers in
+     *     ELContext.
+     * @see StandardELContext#addELResolver
+     */
+    public void addELResolver(ELResolver elr) {
+        getELContext().addELResolver(elr);
+    }
+
+    /**
+     * Maps a static method to an EL function.
+     * @param prefix The namespace of the functions, can be "".
+     * @param function The name of the function.  
+     * @param meth The static method to be invoked when the function is used.
+     */
+    public void mapFunction(String prefix, String function, Method meth) {
+        getELContext().getFunctionMapper().mapFunction(prefix, function, meth);
+    }
+
+    /** 
+     * Assign a ValueExpression to an EL 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.
+     */
+    public Object defineBean(String name, Object bean) {
+        Object ret = getELContext().getBeans().get(name);
+        getELContext().getBeans().put(name, bean);
+        return ret;
+    }
+
+    /**
+     * 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..587c7fd
--- /dev/null
+++ b/api/src/main/java/javax/el/ELProcessor.java
@@ -0,0 +1,312 @@
+/*
+ * 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 javax.el;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * <p>Provides an API for using EL in a stand-alone environment.</p>
+ * 
+ * <p>This class provides a direct and simple interface for
+ * <ul>
+ *   <li>Evaluating EL expressions.</li>
+ *   <li>Assigning values to beans or setting a bean property.</li>
+ *   <li>Setting a {@link ValueExpression} to a EL variable.</li>
+ *   <li>Defining a static method as an EL function.</li>
+ *   <li>Defining an object instance as an EL name.
+ * </ul>
+ * 
+ * <p>This API is not a replacement for the APIs in EL 2.2.  Containers that
+ * maintains EL environments can continue to do so, without using this API.</p>
+ * 
+ * <p>For EL users who want to manipulate EL environments, like adding custom
+ * {@link ELResolver}s, {@link ELManager} can be used.</p>
+ *
+ * <h3>Scope and Life Cycle</h3>
+ * <p>Since it maintains the state of the EL environments,
+ * <code>ELProcessor</code> is not thread safe.  In the simplest case,
+ * an instance can be created and destroyed before and after evaluating
+ * EL expressions.  A more general usage is to use an instance of
+ * <code>ELProcessor</code> for a session, so that the user can configure the
+ * EL evaluation environment for that session.</p>
+ *
+ * <h3>Automatic Bracketing of Expressions</h3>
+ * <p>A note about the EL 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 EL 3.0
+ */
+
+public class ELProcessor {
+
+    private ELManager elManager = new ELManager();
+    private ExpressionFactory factory = elManager.getExpressionFactory();
+
+    /**
+     * Return the ELManager used for EL processing.
+     * @return The ELManager used for EL processing.
+     */
+    public ELManager getELManager() {
+        return elManager;
+    }
+
+    /**
+     * Evaluates an EL expression.
+     * @param expression The EL expression to be evaluated.
+     * @return The result of the expression evaluation.
+     */
+    public Object eval(String expression) {
+        return getValue(expression, Object.class);
+    }
+
+    /**
+     * Evaluates an EL expression, and coerces the result to the specified type.
+     * @param expression The EL 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 an EL expression to an EL variable.  The expression is parsed,
+     * but not evaluated, and the parsed expression is mapped to the EL
+     * 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 EL 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 an EL 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 singnature: " + 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 singnature: " + 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 an EL 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 java.lang.reflect.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..29a74f9
--- /dev/null
+++ b/api/src/main/java/javax/el/ELResolver.java
@@ -0,0 +1,413 @@
+/*
+ * 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.
+ */
+
+package javax.el;
+
+import java.util.Iterator;
+import java.beans.FeatureDescriptor;
+
+/**
+ * Enables customization of variable, property, method call, and type
+ * conversion resolution behavior for EL 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 EL 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>
+ *
+ * <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>
+ *
+ * <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>
+ *
+ * <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>
+ *
+ * <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>
+ * 
+ * <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.</p>
+ *
+ * @see CompositeELResolver
+ * @see ELContext#getELResolver
+ * @since JSP 2.1
+ */
+public abstract class ELResolver {
+    
+    // --------------------------------------------------------- Constants
+
+    /**
+     * <p>The attribute name of the named attribute in the
+     * <code>FeatureDescriptor</code> that specifies the runtime type of
+     * the variable or property.</p>
+     */
+
+    public static final String TYPE = "type";
+
+    /**
+     * <p>The attribute name of the named attribute in the
+     * <code>FeatureDescriptor</code> that specifies whether the
+     * variable or property can be resolved at runtime.</p>
+     */
+
+    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>
+     *
+     * <p>A default implementation is provided that returns null so that
+     * existing classes that extend ELResolver can continue to function.</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).
+     * @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 EL 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>
+     *
+     * <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>
+     *
+     * <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>
+     *
+     * <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.</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
+     * @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 convertion.
+     * @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..65751b3
--- /dev/null
+++ b/api/src/main/java/javax/el/ELUtil.java
@@ -0,0 +1,858 @@
+/*
+ * 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.
+ */
+
+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;
+
+/**
+ *
+ * <p>Utility methods for this portion of the EL implementation</p>
+ *
+ * <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.</p>
+ *
+ * @author edburns
+ * @author Kin-man Chung
+ * @author Dongbin Nie
+ */
+class ELUtil {
+    
+    /**
+     * <p>This class may not be constructed.</p>
+     */
+    
+    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
+     * {@link javax.faces.context.FacesContext} instance for each
+     * processing thread.</p>
+     */
+    private static ThreadLocal<Map<String, ResourceBundle>> instance =
+                new ThreadLocal<Map<String, ResourceBundle>>() {
+            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 (null == result) {
+            result = new HashMap<String, ResourceBundle>();
+            setCurrentInstance(result);
+        }
+        return result;
+
+    }
+    
+    /**
+     * <p>Replace the Map with the argument context.</p>
+     *
+     * @param context the Map to be stored in ThreadLocal storage.
+     */
+
+    private static void setCurrentInstance(Map<String, ResourceBundle> context) {
+
+        instance.set(context);
+
+    }
+    
+    /*
+     * <p>Convenience method, calls through to 
+     * {@link #getExceptionMessageString(javax.el.ELContext,java.lang.String,Object []).
+     * </p>
+     *
+     * @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 EL 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 (null != locale) {
+            Map<String, ResourceBundle> threadMap = getCurrentInstance();
+            ResourceBundle rb = null;
+            if (null == (rb = (ResourceBundle)
+                    threadMap.get(locale.toString()))) {
+                rb = ResourceBundle.getBundle("javax.el.PrivateMessages",
+                                              locale);
+                threadMap.put(locale.toString(), rb);
+            }
+            if (null != rb) {
+                try {
+                    result = rb.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 EL implementation: ???" + messageId + "???";
+                } catch (Exception e) {
+                    result = "Exception resolving message in EL 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<?> c,
+                                    Object[] params) {
+        Object[] parameters = buildParameters(
+                context, c.getParameterTypes(), c.isVarArgs(), params);;
+        try {
+            return c.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 method,
+                             Class<?>[] paramTypes,
+                             Object[] params,
+                             boolean staticOnly) {
+        Method m = findMethod(klass, method, paramTypes, params);
+        if (staticOnly && !Modifier.isStatic(m.getModifiers())) {
+            throw new MethodNotFoundException("Method " + method + "for class "
+                    + klass + " not found or accessible");
+        }
+
+        return m;
+    }
+
+    /*
+     * This method duplicates code in com.sun.el.util.ReflectionUtil. When
+     * making changes keep the code in sync.
+     */
+    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 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.
+     */
+    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 = "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<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 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;
+            } 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 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 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 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.
+     */
+    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..a26c9d9
--- /dev/null
+++ b/api/src/main/java/javax/el/EvaluationListener.java
@@ -0,0 +1,52 @@
+/*
+ * 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 javax.el;
+
+/**
+ * The listener interface for receiving notification when an
+ * EL expression is evaluated.
+ *
+ * @since EL 3.0
+ */
+public abstract class EvaluationListener {
+
+    /**
+     * Receives notification before an EL expression is evaluated
+     * @param context The ELContext
+     * @param expression The EL expression string to be evaluated
+     */
+    public void beforeEvaluation(ELContext context, String expression) {
+    }
+
+    /**
+     * Receives notification after an EL expression is evaluated
+     * @param context The ELContext
+     * @param expression The EL 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..dd6ba50
--- /dev/null
+++ b/api/src/main/java/javax/el/Expression.java
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+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 JSP 2.1
+ */
+public abstract class Expression
+        implements Serializable {
+    // Debugging
+    
+    /**
+     * 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)
+     */
+    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()
+     */
+    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 EL delimeters (<code>${...}</code> or
+     * <code>#{...}</code>).</p>
+     *
+     * @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..8f0e191
--- /dev/null
+++ b/api/src/main/java/javax/el/ExpressionFactory.java
@@ -0,0 +1,329 @@
+/*
+ * 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.
+ */
+
+package javax.el;
+
+import java.util.Map;
+import java.lang.reflect.Method;
+
+import java.util.Properties;
+
+/**
+ * Provides an implementation for creating and evaluating EL expressions.
+ *
+ * <p>Classes that implement the EL 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>
+ * <p>The {@link #newInstance} method can be used to obtain an
+ * instance of the implementation.
+ * Technologies such as
+ * JavaServer Pages and JavaServer Faces provide access to an
+ * implementation via factory methods.</p>
+ *
+ * <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>
+ *
+ * <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>
+ *
+ * <p>The ELContext object also provides access to the {@link FunctionMapper}
+ * and {@link VariableMapper} to be used when parsing the expression.
+ * EL 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>
+ *
+ * <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>
+ *
+ * <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>
+ *
+ * <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></p>
+ *
+ * @since JSP 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>
+     */
+    public static ExpressionFactory newInstance() {
+        return ExpressionFactory.newInstance(null);
+    }
+
+    /**
+     * <p>Create a new instance of a <code>ExpressionFactory</code>, with
+     * optional properties.
+     * This method uses the same lookup procedure as the one used in
+     * <code>newInstance()</code>.
+     * </p>
+     * <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>
+     * <p>
+     * Properties are optional and can be ignored by an implementation.
+     * </p>
+     * <p>The name of a property should start with "javax.el."</p>
+     * <p>
+     * The following are some suggested names for properties.
+     * <ul>
+     * <li>javax.el.cacheSize</li>
+     * </ul></p>
+     *
+     * @param properties Properties passed to the implementation.
+     *     If null, then no properties.
+     */
+    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>.</p>
+     *
+     * @param context The EL 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>.
+     *     <p>
+     *     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.</p>
+     * @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.  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.
+     *
+     * @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.
+     */
+    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>
+     * <p>This method should perform syntactic validation of the expression.
+     * If in doing so it detects errors, it should raise an
+     * <code>ELException</code>.</p>
+     *
+     * @param context The EL 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>.
+     *     <p>
+     *     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.</p>
+     *
+     * @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 arugments in the EL
+     *     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
+     * EL 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.
+     * </p>
+     *
+     * @param obj The object to coerce.
+     * @param targetType The target type for the coercion.
+     * @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 EL.2 for detailed descriptions of these operators, their
+     * arguments, and return values.</p>
+     *
+     * @return The <code>ELResolver</code> that implements the Query Operators.
+     *
+     * @since EL 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 EL 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..6a6e6f2
--- /dev/null
+++ b/api/src/main/java/javax/el/FactoryFinder.java
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ */
+
+package javax.el;
+
+import java.lang.reflect.Constructor;
+import java.io.InputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.Properties;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+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 rd =
+                    new BufferedReader(new InputStreamReader(is, "UTF-8"));
+        
+                String factoryClassName = rd.readLine();
+                rd.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 configFile = javah + File.separator +
+                "lib" + File.separator + "el.properties";
+            File f=new File( configFile );
+            if( f.exists()) {
+                Properties props=new Properties();
+                props.load( new FileInputStream(f));
+                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..c3999c0
--- /dev/null
+++ b/api/src/main/java/javax/el/FunctionMapper.java
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+package javax.el;
+
+import java.lang.reflect.Method;
+
+/**
+ * The interface to a map between EL 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.</p>
+ *
+ * @since JSP 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 EL 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..738031f
--- /dev/null
+++ b/api/src/main/java/javax/el/ImportHandler.java
@@ -0,0 +1,166 @@
+/*
+ * 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 javax.el;
+
+import java.util.Map;
+import java.util.List;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.lang.reflect.Modifier;
+
+/**
+ * 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<String, String>();
+    private Map<String, Class<?>> classMap = new HashMap<String, Class<?>>();
+    private Map<String, String> staticNameMap = new HashMap<String, String>();
+    private HashSet<String> notAClass = new HashSet<String>();
+    private List<String> packages = new ArrayList<String>();
+
+    {
+        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 (Modifier.isAbstract(modifiers) || Modifier.isInterface(modifiers)
+                || ! Modifier.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..5810002
--- /dev/null
+++ b/api/src/main/java/javax/el/LambdaExpression.java
@@ -0,0 +1,158 @@
+/*
+ * 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 javax.el;
+
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.ArrayList;
+
+/**
+ * <p>Encapsulates a parameterized {@link ValueExpression}.</p>
+ *
+ * <p>A <code>LambdaExpression</code> is a representation of the EL 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 EL expression containing
+ * a Lambda expression is evaluated.</p>
+ * <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.</p>
+ * @see ELContext#getLambdaArgument
+ * @see ELContext#enterLambdaScope
+ * @see ELContext#exitLambdaScope
+ */
+
+public class LambdaExpression {
+
+    private List<String> formalParameters = new ArrayList<String>();
+    private ValueExpression expression;
+    private ELContext context;
+    // Arguments from nesting lambdas, when the body is another lambda
+    private Map<String, Object> envirArgs = null;
+
+    /**
+     * 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<String, Object>();
+    }
+
+    /**
+     * 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<String, Object>();
+
+        // 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(this.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..0b5ee1e
--- /dev/null
+++ b/api/src/main/java/javax/el/ListELResolver.java
@@ -0,0 +1,375 @@
+/*
+ * 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.
+ */
+
+package javax.el;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.Collections;
+import java.util.ArrayList;
+import java.beans.FeatureDescriptor;
+
+
+/**
+ * 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 JSP 2.1
+ */
+public class ListELResolver extends ELResolver {
+
+    /**
+     * Creates a new read/write <code>ListELResolver</code>.
+     */
+    public ListELResolver() {
+        this.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.
+     */
+    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.
+     */
+    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.
+     */
+    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) 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;
+            }
+        }
+    }
+
+    static private Class<?> theUnmodifiableListClass =
+        Collections.unmodifiableList(new ArrayList<Object>()).getClass();
+
+    /**
+     * 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.
+     */
+    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>.
+     */
+    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>.
+     */
+    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();
+    }
+
+    private boolean isReadOnly;
+}
+
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..bb2d9da
--- /dev/null
+++ b/api/src/main/java/javax/el/MapELResolver.java
@@ -0,0 +1,377 @@
+/*
+ * 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.
+ */
+
+package javax.el;
+
+import java.beans.FeatureDescriptor;
+import java.util.Map;
+import java.util.Iterator;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * 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 JSP 2.1
+ */
+public class MapELResolver extends ELResolver {
+
+    /**
+     * Creates a new read/write <code>MapELResolver</code>.
+     */
+    public MapELResolver() {
+        this.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.
+     */
+    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.
+     */
+    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;
+    }
+
+    static private Class<?> theUnmodifiableMapClass =
+        Collections.unmodifiableMap(new HashMap<Object, Object>()).getClass();
+
+    /**
+     * 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.
+     */
+    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)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.
+     */
+    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:
+     * <dl>
+     *     <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>
+     * </dl>
+     * In addition, the following named attributes must be set in the
+     * returned <code>FeatureDescriptor</code>s:
+     * <dl>
+     *     <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>
+     * </dl>
+     * </p>
+     * 
+     * @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.
+     */
+    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<FeatureDescriptor>();
+            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", Boolean.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>.
+     */
+    public Class<?> getCommonPropertyType(ELContext context,
+                                       Object base) {
+        if (base != null && base instanceof Map) {
+            return Object.class;
+        }
+        return null;
+    }
+
+    private boolean isReadOnly;
+}
+
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..d6928ab
--- /dev/null
+++ b/api/src/main/java/javax/el/MethodExpression.java
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ */
+
+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 JSP 2.1
+ */
+public abstract class MethodExpression extends Expression
+{
+    // 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 EL 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.</p>
+     *
+     * @return <code>true</code> if the MethodExpression was created with
+     *    parameters, <code>false</code> otherwise.
+     * @since EL 2.2
+     */
+    public boolean isParametersProvided() {
+        return false;
+    }
+
+    /**
+     * Use isParametersProvided instead.
+     */
+    @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..67273ff
--- /dev/null
+++ b/api/src/main/java/javax/el/MethodInfo.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+package javax.el;
+
+/**
+ * Holds information about a method that a {@link MethodExpression} 
+ * evaluated to.
+ *
+ * @since JSP 2.1
+ */
+public class MethodInfo {
+    
+    /** 
+     * 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 this.name;
+    }
+    
+    /**
+     * Returns the return type of the method
+     *
+     * @return the return type of the method
+     */
+    public Class<?> getReturnType() {
+        return this.returnType;
+    }
+    
+    /**
+     * Returns the parameter types of the method
+     *
+     * @return the parameter types of the method
+     */
+    public Class<?>[] getParamTypes() {
+        return this.paramTypes;
+    }
+    
+    private String name;
+    private Class<?> returnType;
+    private Class<?>[] 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..f708088
--- /dev/null
+++ b/api/src/main/java/javax/el/MethodNotFoundException.java
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+package javax.el;
+
+/**
+ * Thrown when a method could not be found while evaluating a
+ * {@link MethodExpression}.
+ *
+ * @see MethodExpression
+ * @since JSP 2.1
+ */
+public class MethodNotFoundException extends ELException {
+
+    /**
+     * 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..5232feb
--- /dev/null
+++ b/api/src/main/java/javax/el/PropertyNotFoundException.java
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+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 JSP 2.1
+ */
+public class PropertyNotFoundException extends ELException {
+
+    //-------------------------------------
+    /**
+     * 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..1d17cf2
--- /dev/null
+++ b/api/src/main/java/javax/el/PropertyNotWritableException.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+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 JSP 2.1
+ */
+public class PropertyNotWritableException extends ELException {
+
+    //-------------------------------------
+    /**
+     * 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..233d648
--- /dev/null
+++ b/api/src/main/java/javax/el/ResourceBundleELResolver.java
@@ -0,0 +1,287 @@
+/*
+ * 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.
+ */
+
+package javax.el;
+
+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>
+ * 
+ * <p>
+ * This resolver is read only and will throw a
+ * {@link PropertyNotWritableException} if <code>setValue</code> is called.
+ * </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.ResourceBundle
+ * @since JSP 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.
+     * </p>
+     * @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.
+     */
+    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>
+     */
+    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.
+     */
+    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 instanceof {@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>
+     */
+    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:
+     * <dl>
+     * <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>
+     * </dl>
+     * In addition, the following named attributes must be set in the returned
+     * <code>FeatureDescriptor</code>s:
+     * <dl>
+     * <li>{@link ELResolver#TYPE} - <code>String.class</code></li>
+     * <li>{@link ELResolver#RESOLVABLE_AT_DESIGN_TIME} - <code>true</code></li>
+     * </dl>
+     * </p>
+     * 
+     * @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.
+     */
+    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
+        if (base instanceof ResourceBundle) {
+            ResourceBundle bundle = (ResourceBundle) base;
+            List<FeatureDescriptor> features = new ArrayList<FeatureDescriptor>();
+            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, Boolean.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>.
+     */
+    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..fa901fc
--- /dev/null
+++ b/api/src/main/java/javax/el/StandardELContext.java
@@ -0,0 +1,284 @@
+/*
+ * 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 javax.el;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.lang.reflect.Method;
+
+/**
+ * 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 EL 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-confured 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 = null;
+ 
+    /**
+     * A bean repository local to this context
+     */
+    private Map<String, Object> beans = new HashMap<String, Object>();
+
+    /**
+     * Construct a default ELContext for a stand-alone environment.
+     * @param factory The ExpressionFactory 
+     */
+    public StandardELContext(ExpressionFactory factory) {
+        this.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) {
+        this.delegate = context;
+        // Copy all attributes except map and resolved
+        CompositeELResolver elr = new CompositeELResolver();
+        elr.add(new BeanNameELResolver(new LocalBeanNameResolver()));
+        customResolvers = new CompositeELResolver();
+        elr.add(customResolvers);
+        elr.add(context.getELResolver());
+        elResolver = elr;
+
+        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 delegate.getContext(key);
+        } else {
+            return super.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>
+     * </p>
+     * @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 = null;
+
+        DefaultFunctionMapper(Map<String, Method> initMap){
+            functions = (initMap == null)?
+                               new HashMap<String, Method>():
+                               new HashMap<String, Method>(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 = null;
+
+        @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<String, ValueExpression>();
+            }
+            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..173b6ee
--- /dev/null
+++ b/api/src/main/java/javax/el/StaticFieldELResolver.java
@@ -0,0 +1,294 @@
+/*
+ * 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 javax.el;
+
+import java.util.Iterator;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Constructor;
+
+import java.beans.FeatureDescriptor;
+
+/**
+ * <p>An {@link ELResolver} for resolving static fields, enum constants and 
+ * static methods.  Also handles constructor calls as a special case.</p>
+ * <p>The resolver handles base objects of the type {@link ELClass}, which
+ * is usually generated by an EL implementation.</p>
+ *
+ * @see ELClass
+ * @since EL 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
+     */
+    @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(
+                        ELUtil.getExceptionMessageString(context,
+                            "staticFieldWriteError",
+                            new Object[] { klass.getName(), fieldName}));
+        }
+    }
+
+    /**
+     * <p>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>
+     * <p>Invoke the public static method specified by <code>method</code>.</p>
+     * <p>The process involved in the method selection is
+     * the same as that used in {@link BeanELResolver}.</p>
+     *
+     * <p>As a special case, if the name of the method is "&lt;init>", the
+     * constructor for the class will be invoked.</p>
+     *
+     * @param base An <code>ELClass</code>
+     * @param method 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 method,
+                         Class<?>[] paramTypes,
+                         Object[] params) {
+
+        if (context == null) {
+            throw new NullPointerException();
+        }
+
+        if (!(base instanceof ELClass && method instanceof String)) {
+            return null;
+        }
+
+        Class<?> klass = ((ELClass)base).getKlass();
+        String name = (String) method;
+
+        Object ret;
+        if ("<init>".equals(name)) {
+            Constructor<?> constructor =
+                ELUtil.findConstructor(klass, paramTypes, params);
+            ret = ELUtil.invokeConstructor(context, constructor, params);
+        } else {
+            Method meth =
+                ELUtil.findMethod(klass, name, paramTypes, params, true);
+            ret = ELUtil.invokeMethod(context, meth, null, params);
+        }
+        context.setPropertyResolved(base, method);
+        return ret;
+    }
+
+    /**
+     * <p>Returns the type of a static field.</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>
+     *
+     * If the property string is a public static field of class specified in
+     * ELClass, return the type of the static field.</p>
+     * @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 (Modifier.isPublic(mod) && Modifier.isStatic(mod)) {
+                    return field.getType();
+                }
+            } catch (NoSuchFieldException ex) {
+            }
+            throw new PropertyNotFoundException(
+                        ELUtil.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) {
+            Class<?> klass = ((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..8a6d86f
--- /dev/null
+++ b/api/src/main/java/javax/el/TypeConverter.java
@@ -0,0 +1,100 @@
+/*
+ * 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 javax.el;
+
+import java.util.Iterator;
+import java.beans.FeatureDescriptor;
+
+/**
+ * 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
+ * <blockquote><pre>
+ *     ELProcessor elp = new ELProcessor();
+ *     elp.getELManager().addELResolver(new TypeConverter() {
+ *         Object convertToType(ELContext context, Object obj, Class<?> type) {
+ *             if ((obj instanceof String) && type == MyDate.class) {
+ *                 context.setPropertyResolved(obj, type);
+ *                 return (obj == null)? null: new MyDate(obj.toString());
+ *             }
+ *             return null;
+ *         }
+ *      };
+ * </pre></blockquote>
+ *
+ * @since EL 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..433c210
--- /dev/null
+++ b/api/src/main/java/javax/el/ValueExpression.java
@@ -0,0 +1,185 @@
+/*
+ * 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.
+ */
+
+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 JSP 2.1
+ */
+public abstract class ValueExpression
+    extends Expression
+{
+    
+    /**
+     * 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.</p>
+     *
+     * @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.</p>
+     *
+     * @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 EL variable, return
+     * the <code>ValueReference</code> for the
+     * <code>ValueExpression</code> associated with this EL variable.
+     *
+     * @since EL 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..b5e5ce7
--- /dev/null
+++ b/api/src/main/java/javax/el/ValueReference.java
@@ -0,0 +1,46 @@
+/*
+ * 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
+ */
+
+package javax.el;
+
+import java.io.Serializable;
+
+/**
+ * This encapsulates a base model object and one of its properties.
+ *
+ * @since EL 2.2
+ */
+
+public class ValueReference implements Serializable {
+
+    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..0aae995
--- /dev/null
+++ b/api/src/main/java/javax/el/VariableMapper.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+package javax.el;
+
+/**
+ * The interface to a map between EL variables and the EL expressions
+ * they are associated with.
+ *
+ * @since JSP 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 EL 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..d66fe00
--- /dev/null
+++ b/api/src/main/java/javax/el/package.html
@@ -0,0 +1,243 @@
+<!--
+
+    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 the <strong>Unified Expression Language 3.0</strong>
+
+<p>The Expression Language (EL) is a simple language originally designed to
+satisfy the specific needs of web application developers. It has evloved
+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 Expression Language engine. The API
+is logically partitioned as follows:
+
+<ul>
+  <li><a href="#Context">EL 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">EL Functions</a></li>
+  <li><a href="#Variables">EL Variables</a></li>
+  <li><a href="#Standalone">EL in Stand-alone environment</li>
+</ul>
+
+<h3><a name="Context">EL Context</a></h3>
+
+<p>An important goal of the EL 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 EL 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 EL used in a web container, the creation of <code>ELContext
+</code> objects is controlled through the underlying technology.
+For example, in JSP, 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 the EL: <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 EL expression can represent a method
+invocation, such as <code>${trader.buy("JAVA")}</code>, where the arugments 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>. Morevover, 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 the EL.</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 an EL function 
+(for example <code>${fn:toUpperCase(customer.name)}</code>) or an
+EL variable, then  
+{@link javax.el.FunctionMapper} and {@link javax.el.VariableMapper}
+objects must be available within the <code>ELContext</code> so that EL functions and
+EL 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 JSF request processing life cycle, EL 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 EL 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, the EL 
+features a pluggable mechanism 
+to resolve model object references as well as properties and method
+invocations of these objects.</p>
+
+<p>The EL 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">EL Functions</a></h3>
+
+<p>If an EL 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">EL Variables</a></h3>
+
+<p>Just like {@link javax.el.FunctionMapper} provides
+a flexible mechanism to add functions to the EL, {@link javax.el.VariableMapper}
+provides a flexible mechanism to support the notion of 
+<strong>EL variables</strong>.
+</p>
+ 
+<p>
+An EL variable does not directly refer to a model object that can then
+be resolved by an <code>ELResolver</code>. Instead, it refers to an EL
+expression. The evaluation of that EL expression gives the EL 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 an EL 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 an EL variable because it does not refer directly to a model
+object.  Instead, it refers to another EL expression, namely a
+specific item in the collection referred to by the EL 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 an EL variable to the EL expression from which it gets its value.
+</p>
+
+<h3><a name="Standalone">EL in Stand-alone environment</a></h3>
+<p>EL 3.0 includes APIs for using EL 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 a beans locally.</p>
+
+<p>{@link javax.el.ELManager} provides a lower level APIs for managing the EL
+parsing and evaluation environment.  It contains a default ELContext
+{@link javax.el.StandardELContext}.</p>
+
+</body>
+</html>
+
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/src/main/java/com/sun/el/ExpressionFactoryImpl.java b/impl/src/main/java/com/sun/el/ExpressionFactoryImpl.java
new file mode 100644
index 0000000..24d1dc6
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/ExpressionFactoryImpl.java
@@ -0,0 +1,118 @@
+/*
+ * 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.util.Map;
+import java.util.HashMap;
+import java.util.Properties;
+import java.lang.reflect.Method;
+
+import javax.el.ELContext;
+import javax.el.ELException;
+import javax.el.ExpressionFactory;
+import javax.el.MethodExpression;
+import javax.el.ValueExpression;
+import javax.el.ELResolver;
+
+import com.sun.el.lang.ExpressionBuilder;
+import com.sun.el.lang.ELSupport;
+import com.sun.el.util.MessageFactory;
+import com.sun.el.stream.StreamELResolver;
+
+/**
+ * @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 {
+
+    /**
+     * 
+     */
+    public ExpressionFactoryImpl() {
+        super();
+    }
+
+    public ExpressionFactoryImpl(Properties properties) {
+        super();
+        this.properties = properties;
+        this.isBackwardCompatible22 = "true".equals(getProperty("javax.el.bc2.2"));
+    }
+
+    public Object coerceToType(Object obj, Class type) {
+        Object ret;
+        try {
+            ret = ELSupport.coerceToType(obj, type, isBackwardCompatible22);
+        } catch (IllegalArgumentException ex) {
+            throw new ELException(ex);
+        }
+        return ret;
+    }
+
+    public MethodExpression createMethodExpression(ELContext context,
+            String expression, Class expectedReturnType,
+            Class[] expectedParamTypes) {
+        ExpressionBuilder builder = new ExpressionBuilder(expression, context);
+        MethodExpression me = builder.createMethodExpression(expectedReturnType,
+                expectedParamTypes);
+        if (expectedParamTypes == null && !me.isParametersProvided()) {
+            throw new NullPointerException(MessageFactory
+                    .get("error.method.nullParms"));
+        }
+        return me;
+    }
+
+    public ValueExpression createValueExpression(ELContext context,
+            String expression, Class expectedType) {
+        if (expectedType == null) {
+            throw new NullPointerException(MessageFactory
+                    .get("error.value.expectedType"));
+        }
+        ExpressionBuilder builder = new ExpressionBuilder(expression, context);
+        return builder.createValueExpression(expectedType);
+    }
+
+    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() {
+        Map<String, Method> funcs = new HashMap<String, Method>();
+        return funcs;
+    }
+
+    private Properties properties;
+    private boolean isBackwardCompatible22;
+}
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..2f98e8b
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/MethodExpressionImpl.java
@@ -0,0 +1,326 @@
+/*
+ * 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.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 javax.el.EvaluationListener;
+
+import com.sun.el.lang.ELSupport;
+import com.sun.el.lang.EvaluationContext;
+import com.sun.el.lang.ExpressionBuilder;
+import com.sun.el.parser.Node;
+import com.sun.el.util.ReflectionUtil;
+
+/**
+ * An <code>Expression</code> that refers to a method on an object.
+ * 
+ * <p>
+ * <code>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 transient Node node;
+
+    private Class[] paramTypes;
+
+    /**
+     * 
+     */
+    public MethodExpressionImpl() {
+        super();
+    }
+
+    /**
+     * @param expr
+     * @param node
+     * @param fnMapper
+     * @param expectedType
+     * @param paramTypes
+     */
+    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)
+     */
+    public boolean equals(Object obj) {
+        if (obj instanceof MethodExpressionImpl) {
+            MethodExpressionImpl me = (MethodExpressionImpl) obj;
+            return getNode().equals(me.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()
+     */
+    public String getExpressionString() {
+        return this.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)
+     */
+    public MethodInfo getMethodInfo(ELContext context)
+            throws PropertyNotFoundException, MethodNotFoundException,
+            ELException {
+        Node n = this.getNode();
+        EvaluationContext ctx = new EvaluationContext(context, this.fnMapper,
+                this.varMapper);
+        return n.getMethodInfo(ctx, this.paramTypes);
+    }
+
+    /**
+     * @return The Node for the expression
+     * @throws ELException
+     */
+    private Node getNode() throws ELException {
+        if (this.node == null) {
+            this.node = ExpressionBuilder.createNode(this.expr);
+        }
+        return this.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()
+     */
+    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[])
+     */
+    public Object invoke(ELContext context, Object[] params)
+            throws PropertyNotFoundException, MethodNotFoundException,
+            ELException {
+        EvaluationContext ctx = new EvaluationContext(context, this.fnMapper,
+                this.varMapper);
+        ctx.notifyBeforeEvaluation(this.expr);
+        Object obj = this.getNode().invoke(ctx, this.paramTypes, params);
+        ctx.notifyAfterEvaluation(this.expr);
+        return obj;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.io.Externalizable#readExternal(java.io.ObjectInput)
+     */
+    public void readExternal(ObjectInput in) throws IOException,
+            ClassNotFoundException {
+        this.expr = in.readUTF();
+        String type = in.readUTF();
+        if (!"".equals(type)) {
+            this.expectedType = ReflectionUtil.forName(type);
+        }
+        this.paramTypes = ReflectionUtil.toTypeArray(((String[]) in
+                .readObject()));
+        this.fnMapper = (FunctionMapper) in.readObject();
+        this.varMapper = (VariableMapper) in.readObject();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
+     */
+    public void writeExternal(ObjectOutput out) throws IOException {
+        out.writeUTF(this.expr);
+        out.writeUTF((this.expectedType != null) ? this.expectedType.getName()
+                : "");
+        out.writeObject(ReflectionUtil.toTypeNameArray(this.paramTypes));
+        out.writeObject(this.fnMapper);
+        out.writeObject(this.varMapper);
+    }
+
+    public boolean isLiteralText() {
+        return false;
+    }
+
+    @Override
+    public boolean isParametersProvided() {
+        return this.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..507076f
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/MethodExpressionLiteral.java
@@ -0,0 +1,98 @@
+/*
+ * 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.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.lang.ELSupport;
+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;
+    }
+
+    public MethodInfo getMethodInfo(ELContext context) throws ELException {
+        return new MethodInfo(this.expr, this.expectedType, this.paramTypes);
+    }
+
+    public Object invoke(ELContext context, Object[] params) throws ELException {
+        if (this.expectedType == null) {
+            return this.expr;
+        }
+
+        try {
+            return context.convertToType(this.expr, this.expectedType);
+        } catch (Exception ex) {
+            throw new ELException (ex);
+        }
+    }
+
+    public String getExpressionString() {
+        return this.expr;
+    }
+
+    public boolean equals(Object obj) {
+        return (obj instanceof MethodExpressionLiteral && this.hashCode() == obj.hashCode());
+    }
+
+    public int hashCode() {
+        return this.expr.hashCode();
+    }
+
+    public boolean isLiteralText() {
+        return true;
+    }
+
+    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+        this.expr = in.readUTF();
+        String type = in.readUTF();
+        if (!"".equals(type)) {
+            this.expectedType = ReflectionUtil.forName(type);
+        }
+        this.paramTypes = ReflectionUtil.toTypeArray(((String[]) in
+                .readObject()));
+    }
+
+    public void writeExternal(ObjectOutput out) throws IOException {
+        out.writeUTF(this.expr);
+        out.writeUTF((this.expectedType != null) ? this.expectedType.getName()
+                : "");
+        out.writeObject(ReflectionUtil.toTypeNameArray(this.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..ec99876
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/ValueExpressionImpl.java
@@ -0,0 +1,284 @@
+/*
+ * 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.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.VariableMapper;
+import javax.el.ValueReference;
+import javax.el.EvaluationListener;
+
+import com.sun.el.lang.ELSupport;
+import com.sun.el.lang.EvaluationContext;
+import com.sun.el.lang.ExpressionBuilder;
+import com.sun.el.parser.AstLiteralExpression;
+import com.sun.el.parser.Node;
+import com.sun.el.util.ReflectionUtil;
+
+/**
+ * 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>
+ * <code>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)
+     */
+    public boolean equals(Object obj) {
+        if (obj instanceof ValueExpressionImpl) {
+            ValueExpressionImpl v = (ValueExpressionImpl) obj;
+            return getNode().equals(v.getNode());
+        }
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see javax.el.ValueExpression#getExpectedType()
+     */
+    public Class getExpectedType() {
+        return this.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()
+     */
+    public String getExpressionString() {
+        return this.expr;
+    }
+
+    /**
+     * @return The Node for the expression
+     * @throws ELException
+     */
+    private Node getNode() throws ELException {
+        if (this.node == null) {
+            this.node = ExpressionBuilder.createNode(this.expr);
+        }
+        return this.node;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see javax.el.ValueExpression#getType(javax.el.ELContext)
+     */
+    public Class getType(ELContext context) throws PropertyNotFoundException,
+            ELException {
+        EvaluationContext ctx = new EvaluationContext(context, this.fnMapper,
+                this.varMapper);
+        return this.getNode().getType(ctx);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see javax.el.ValueExpression#getValueReference(javax.el.ELContext)
+     */
+    public ValueReference getValueReference(ELContext context)
+            throws PropertyNotFoundException, ELException {
+        EvaluationContext ctx = new EvaluationContext(context, this.fnMapper,
+                this.varMapper);
+        return this.getNode().getValueReference(ctx);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see javax.el.ValueExpression#getValue(javax.el.ELContext)
+     */
+    public Object getValue(ELContext context) throws PropertyNotFoundException,
+            ELException {
+        EvaluationContext ctx = new EvaluationContext(context, this.fnMapper,
+                this.varMapper);
+        ctx.notifyBeforeEvaluation(this.expr);
+        Object value = this.getNode().getValue(ctx);
+        if (this.expectedType != null) {
+            try {
+                value = ctx.convertToType(value, this.expectedType);
+            } catch (IllegalArgumentException ex) {
+                throw new ELException(ex);
+            }
+        }
+        ctx.notifyAfterEvaluation(this.expr);
+        return value;
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.lang.Object#hashCode()
+     */
+    public int hashCode() {
+        return getNode().hashCode();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see javax.el.ValueExpression#isLiteralText()
+     */
+    public boolean isLiteralText() {
+        try {
+            return this.getNode() instanceof AstLiteralExpression;
+        } catch (ELException ele) {
+            return false;
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see javax.el.ValueExpression#isReadOnly(javax.el.ELContext)
+     */
+    public boolean isReadOnly(ELContext context)
+            throws PropertyNotFoundException, ELException {
+        EvaluationContext ctx = new EvaluationContext(context, this.fnMapper,
+                this.varMapper);
+        return this.getNode().isReadOnly(ctx);
+    }
+
+    public void readExternal(ObjectInput in) throws IOException,
+            ClassNotFoundException {
+        this.expr = in.readUTF();
+        String type = in.readUTF();
+        if (!"".equals(type)) {
+            this.expectedType = ReflectionUtil.forName(type);
+        }
+        this.fnMapper = (FunctionMapper) in.readObject();
+        this.varMapper = (VariableMapper) in.readObject();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see javax.el.ValueExpression#setValue(javax.el.ELContext,
+     *      java.lang.Object)
+     */
+    public void setValue(ELContext context, Object value)
+            throws PropertyNotFoundException, PropertyNotWritableException,
+            ELException {
+        EvaluationContext ctx = new EvaluationContext(context, this.fnMapper,
+                this.varMapper);
+        this.getNode().setValue(ctx, value);
+    }
+
+    public void writeExternal(ObjectOutput out) throws IOException {
+        out.writeUTF(this.expr);
+        out.writeUTF((this.expectedType != null) ? this.expectedType.getName()
+                : "");
+        out.writeObject(this.fnMapper);
+        out.writeObject(this.varMapper);
+    }
+
+    public String toString() {
+        return "ValueExpression["+this.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..82ed197
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/ValueExpressionLiteral.java
@@ -0,0 +1,116 @@
+/*
+ * 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.io.Externalizable;
+import java.io.IOException;
+import javax.el.ELContext;
+import javax.el.ELException;
+import javax.el.PropertyNotWritableException;
+
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+import javax.el.ValueExpression;
+
+import com.sun.el.lang.ELSupport;
+import com.sun.el.util.MessageFactory;
+import com.sun.el.util.ReflectionUtil;
+
+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;
+    }
+
+    public Object getValue(ELContext context) {
+        if (this.expectedType != null) {
+            try {
+                return context.convertToType(this.value, this.expectedType);
+            } catch (IllegalArgumentException ex) {
+                throw new ELException(ex);
+            }
+        }
+        return this.value;
+    }
+
+    public void setValue(ELContext context, Object value) {
+        throw new PropertyNotWritableException(MessageFactory.get(
+                "error.value.literal.write", this.value));
+    }
+
+    public boolean isReadOnly(ELContext context) {
+        return true;
+    }
+
+    public Class getType(ELContext context) {
+        return (this.value != null) ? this.value.getClass() : null;
+    }
+
+    public Class getExpectedType() {
+        return this.expectedType;
+    }
+
+    public String getExpressionString() {
+        return (this.value != null) ? this.value.toString() : null;
+    }
+
+    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))));
+    }
+
+    public int hashCode() {
+        return (this.value != null) ? this.value.hashCode() : 0;
+    }
+
+    public boolean isLiteralText() {
+        return true;
+    }
+
+    public void writeExternal(ObjectOutput out) throws IOException {
+        out.writeObject(this.value);
+        out.writeUTF((this.expectedType != null) ? this.expectedType.getName()
+                : "");
+    }
+
+    public void readExternal(ObjectInput in) throws IOException,
+            ClassNotFoundException {
+        this.value = in.readObject();
+        String type = in.readUTF();
+        if (!"".equals(type)) {
+            this.expectedType = ReflectionUtil.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..c832f8a
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/lang/ELArithmetic.java
@@ -0,0 +1,376 @@
+/*
+ * 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.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.sun.el.util.MessageFactory;
+
+/**
+ * A helper class of Arithmetic defined by the EL 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 {
+
+        protected Number add(Number num0, Number num1) {
+            return ((BigDecimal) num0).add((BigDecimal) num1);
+        }
+
+        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());
+        }
+
+        protected Number coerce(String str) {
+            return new BigDecimal(str);
+        }
+
+        protected Number divide(Number num0, Number num1) {
+            return ((BigDecimal) num0).divide((BigDecimal) num1,
+                    BigDecimal.ROUND_HALF_UP);
+        }
+
+        protected Number subtract(Number num0, Number num1) {
+            return ((BigDecimal) num0).subtract((BigDecimal) num1);
+        }
+
+        protected Number mod(Number num0, Number num1) {
+            return Double.valueOf(num0.doubleValue() % num1.doubleValue());
+        }
+
+        protected Number multiply(Number num0, Number num1) {
+            return ((BigDecimal) num0).multiply((BigDecimal) num1);
+        }
+
+        public boolean matches(Object obj0, Object obj1) {
+            return (obj0 instanceof BigDecimal || obj1 instanceof BigDecimal);
+        }
+    }
+
+    public final static class BigIntegerDelegate extends ELArithmetic {
+
+        protected Number add(Number num0, Number num1) {
+            return ((BigInteger) num0).add((BigInteger) num1);
+        }
+
+        protected Number coerce(Number num) {
+            if (num instanceof BigInteger)
+                return num;
+            return new BigInteger(num.toString());
+        }
+
+        protected Number coerce(String str) {
+            return new BigInteger(str);
+        }
+
+        protected Number divide(Number num0, Number num1) {
+            return (new BigDecimal((BigInteger) num0)).divide(new BigDecimal((BigInteger) num1), BigDecimal.ROUND_HALF_UP);
+        }
+
+        protected Number multiply(Number num0, Number num1) {
+            return ((BigInteger) num0).multiply((BigInteger) num1);
+        }
+
+        protected Number mod(Number num0, Number num1) {
+            return ((BigInteger) num0).mod((BigInteger) num1);
+        }
+
+        protected Number subtract(Number num0, Number num1) {
+            return ((BigInteger) num0).subtract((BigInteger) num1);
+        }
+
+        public boolean matches(Object obj0, Object obj1) {
+            return (obj0 instanceof BigInteger || obj1 instanceof BigInteger);
+        }
+    }
+
+    public final static class DoubleDelegate extends ELArithmetic {
+
+        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()));
+        	} else if (num1 instanceof BigDecimal) {
+        		return ((new BigDecimal(num0.doubleValue()).add((BigDecimal) num1)));
+        	}
+            return Double.valueOf(num0.doubleValue() + num1.doubleValue());
+        }
+
+        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());
+        }
+
+        protected Number coerce(String str) {
+            return Double.valueOf(str);
+        }
+
+        protected Number divide(Number num0, Number num1) {
+            return Double.valueOf(num0.doubleValue() / num1.doubleValue());
+        }
+
+        protected Number mod(Number num0, Number num1) {
+            return Double.valueOf(num0.doubleValue() % num1.doubleValue());
+        }
+
+        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()));
+        	} else if (num1 instanceof BigDecimal) {
+        		return ((new BigDecimal(num0.doubleValue()).subtract((BigDecimal) num1)));
+        	}
+            return Double.valueOf(num0.doubleValue() - num1.doubleValue());
+        }
+
+        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()));
+        	} else if (num1 instanceof BigDecimal) {
+        		return ((new BigDecimal(num0.doubleValue()).multiply((BigDecimal) num1)));
+        	}
+            return Double.valueOf(num0.doubleValue() * num1.doubleValue());
+        }
+
+        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 {
+
+        protected Number add(Number num0, Number num1) {
+            return Long.valueOf(num0.longValue() + num1.longValue());
+        }
+
+        protected Number coerce(Number num) {
+            if (num instanceof Long)
+                return num;
+            return Long.valueOf(num.longValue());
+        }
+
+        protected Number coerce(String str) {
+            return Long.valueOf(str);
+        }
+
+        protected Number divide(Number num0, Number num1) {
+            return Long.valueOf(num0.longValue() / num1.longValue());
+        }
+
+        protected Number mod(Number num0, Number num1) {
+            return Long.valueOf(num0.longValue() % num1.longValue());
+        }
+
+        protected Number subtract(Number num0, Number num1) {
+            return Long.valueOf(num0.longValue() - num1.longValue());
+        }
+
+        protected Number multiply(Number num0, Number num1) {
+            return Long.valueOf(num0.longValue() * num1.longValue());
+        }
+
+        public boolean matches(Object obj0, Object obj1) {
+            return (obj0 instanceof Long || obj1 instanceof Long);
+        }
+    }
+
+    public final static BigDecimalDelegate BIGDECIMAL = new BigDecimalDelegate();
+
+    public final static BigIntegerDelegate BIGINTEGER = new BigIntegerDelegate();
+
+    public final static DoubleDelegate DOUBLE = new DoubleDelegate();
+
+    public final static LongDelegate LONG = new LongDelegate();
+
+    private final static 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..945bf44
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/lang/ELSupport.java
@@ -0,0 +1,504 @@
+/*
+ * 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.ELContext;
+import javax.el.ELException;
+import javax.el.PropertyNotFoundException;
+
+import com.sun.el.util.MessageFactory;
+
+/**
+ * A helper class that implements the EL 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 EvaluationException
+     */
+    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 EvaluationException
+     */
+    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 to EL 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..26770bb
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/lang/EvaluationContext.java
@@ -0,0 +1,149 @@
+/*
+ * 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.EventListener;
+import java.util.List;
+import java.util.Map;
+
+import javax.el.ELContext;
+import javax.el.ELResolver;
+import javax.el.FunctionMapper;
+import javax.el.VariableMapper;
+import javax.el.TypeConverter;
+import javax.el.ImportHandler;
+import javax.el.EvaluationListener;
+
+/**
+ * The context for EL expression evaluation.  This wrapper ELContext captures
+ * the function mapper and the variable mapper at the point when the epxression
+ * 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 this.elContext;
+    }
+
+    @Override
+    public FunctionMapper getFunctionMapper() {
+        return this.fnMapper;
+    }
+
+    @Override
+    public VariableMapper getVariableMapper() {
+        return this.varMapper;
+    }
+
+    @Override
+    public Object getContext(Class key) {
+        return this.elContext.getContext(key);
+    }
+
+    @Override
+    public ELResolver getELResolver() {
+        return this.elContext.getELResolver();
+    }
+
+    @Override
+    public boolean isPropertyResolved() {
+        return this.elContext.isPropertyResolved();
+    }
+
+    @Override
+    public void putContext(Class key, Object contextObject) {
+        this.elContext.putContext(key, contextObject);
+    }
+
+    @Override
+    public void setPropertyResolved(boolean resolved) {
+        this.elContext.setPropertyResolved(resolved);
+    }
+
+    @Override
+    public void setPropertyResolved(Object base, Object property) {
+        this.elContext.setPropertyResolved(base, property);
+    }
+
+    @Override
+    public void addEvaluationListener(EvaluationListener listener) {
+        this.elContext.addEvaluationListener(listener);
+    }
+
+    @Override
+    public List<EvaluationListener> getEvaluationListeners() {
+        return this.elContext.getEvaluationListeners();
+    }
+
+    @Override
+    public void notifyBeforeEvaluation(String expr) {
+        this.elContext.notifyBeforeEvaluation(expr);
+    }
+
+    @Override
+    public void notifyAfterEvaluation(String expr) {
+        this.elContext.notifyAfterEvaluation(expr);
+    }
+
+    @Override
+    public void notifyPropertyResolved(Object base, Object property) {
+        this.elContext.notifyPropertyResolved(base, property);
+    }
+
+    @Override
+    public boolean isLambdaArgument(String arg) {
+        return this.elContext.isLambdaArgument(arg);
+    }
+
+    @Override
+    public Object getLambdaArgument(String arg) {
+        return this.elContext.getLambdaArgument(arg);
+    }
+
+    @Override
+    public void enterLambdaScope(Map<String,Object> args) {
+        this.elContext.enterLambdaScope(args);
+    }
+
+    @Override
+    public void exitLambdaScope() {
+        this.elContext.exitLambdaScope();
+    }
+
+    @Override
+    public Object convertToType(Object obj, Class<?> targetType) {
+        return this.elContext.convertToType(obj, targetType);
+    }
+
+    @Override
+    public ImportHandler getImportHandler() {
+        return this.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..17552d1
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/lang/ExpressionBuilder.java
@@ -0,0 +1,291 @@
+/*
+ * 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.reflect.Method;
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+import java.lang.ref.ReferenceQueue;
+import java.util.Map;
+import java.util.Collections;
+import java.util.concurrent.ConcurrentMap;
+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 final static Node createNode(String expr) throws ELException {
+        Node n = createNodeInternal(expr);
+        return n;
+    }
+
+    private final 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)
+     */
+    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 EL 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..49c7516
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/lang/FunctionMapperFactory.java
@@ -0,0 +1,58 @@
+/*
+ * 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)
+     */
+    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..34325ec
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/lang/FunctionMapperImpl.java
@@ -0,0 +1,198 @@
+/*
+ * 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)
+     */
+    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)
+     */
+    public void writeExternal(ObjectOutput out) throws IOException {
+        out.writeObject(this.functions);
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.io.Externalizable#readExternal(java.io.ObjectInput)
+     */
+    // Safe cast
+    @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)
+         */
+        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)
+         */
+        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)
+         */
+        public boolean equals(Object obj) {
+            if (obj instanceof Function) {
+                return this.hashCode() == obj.hashCode();
+            }
+            return false;
+        }
+        
+        /* (non-Javadoc)
+         * @see java.lang.Object#hashCode()
+         */
+        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..c6d6bab
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/lang/VariableMapperFactory.java
@@ -0,0 +1,55 @@
+/*
+ * 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;
+    }
+
+    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;
+    }
+
+    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..dac562f
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/lang/VariableMapperImpl.java
@@ -0,0 +1,58 @@
+/*
+ * 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();
+    }
+
+    public ValueExpression resolveVariable(String variable) {
+        return this.vars.get(variable);
+    }
+
+    public ValueExpression setVariable(String variable,
+            ValueExpression expression) {
+        return this.vars.put(variable, expression);
+    }
+
+    // Safe cast.
+    @SuppressWarnings("unchecked")
+    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+        this.vars = (Map<String, ValueExpression>) in.readObject();
+    }
+
+    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..f776245
--- /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);
+    }
+
+    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..04622b6
--- /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);
+    }
+
+    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..f9f7c36
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstAssign.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 javax.el.VariableMapper;
+import com.sun.el.ValueExpressionImpl;
+import com.sun.el.lang.EvaluationContext;
+
+public
+class AstAssign extends SimpleNode {
+    public AstAssign(int id) {
+        super(id);
+    }
+
+    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..9c72d80
--- /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);
+    }
+
+    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..1bb9591
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstChoice.java
@@ -0,0 +1,70 @@
+/*
+ * 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);
+    }
+
+    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);
+    }
+
+    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);
+    }
+
+    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);
+    }
+
+    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);
+    }
+
+    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..4c624f6
--- /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);
+    }
+
+    public Class getType(EvaluationContext ctx)
+            throws ELException {
+        return String.class;
+    }
+
+    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..00e1097
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstConcat.java
@@ -0,0 +1,37 @@
+/*
+ * 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);
+    }
+
+    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..977930f
--- /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);
+    }
+
+    public Class getType(EvaluationContext ctx)
+            throws ELException {
+        return this.children[0].getType(ctx);
+    }
+
+    public Object getValue(EvaluationContext ctx)
+            throws ELException {
+        return this.children[0].getValue(ctx);
+    }
+
+    public boolean isReadOnly(EvaluationContext ctx)
+            throws ELException {
+        return this.children[0].isReadOnly(ctx);
+    }
+
+    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..aa677e0
--- /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);
+    }
+
+    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..abd69ca
--- /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);
+    }
+
+    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..653ec2a
--- /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);
+    }
+
+    public Class getType(EvaluationContext ctx)
+            throws ELException {
+        return this.children[0].getType(ctx);
+    }
+
+    public Object getValue(EvaluationContext ctx)
+            throws ELException {
+        return this.children[0].getValue(ctx);
+    }
+
+    public boolean isReadOnly(EvaluationContext ctx)
+            throws ELException {
+        return this.children[0].isReadOnly(ctx);
+    }
+
+    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..0b42bee
--- /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);
+    }
+
+    public Class getType(EvaluationContext ctx)
+            throws ELException {
+        return Boolean.class;
+    }
+
+    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..a799058
--- /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);
+    }
+
+    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..71b51b9
--- /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);
+    }
+
+    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..b416907
--- /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;
+    }
+
+    public Object getValue(EvaluationContext ctx)
+            throws ELException {
+        return this.getFloatingPoint();
+    }
+
+    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..f89bf9a
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstFunction.java
@@ -0,0 +1,206 @@
+/*
+ * 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.ELException;
+import javax.el.FunctionMapper;
+import javax.el.LambdaExpression;
+import javax.el.ValueExpression;
+import javax.el.VariableMapper;
+import javax.el.ELClass;
+
+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 EL 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..adce813
--- /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);
+    }
+
+    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..af6dc71
--- /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);
+    }
+
+    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..1ce74ba
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstIdentifier.java
@@ -0,0 +1,215 @@
+/*
+ * 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.ELResolver;
+import javax.el.MethodExpression;
+import javax.el.MethodInfo;
+import javax.el.MethodNotFoundException;
+import javax.el.ValueExpression;
+import javax.el.VariableMapper;
+import javax.el.ValueReference;
+import javax.el.PropertyNotWritableException;
+import javax.el.ELClass;
+
+import com.sun.el.lang.EvaluationContext;
+import com.sun.el.lang.ELSupport;
+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;
+    }
+
+    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;
+    }
+
+    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;
+    }
+
+    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 final 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());
+        }
+    }
+
+    public Object invoke(EvaluationContext ctx, Class[] paramTypes,
+            Object[] paramValues) throws ELException {
+        return this.getMethodExpression(ctx).invoke(ctx.getELContext(), paramValues);
+    }
+    
+
+    public MethodInfo getMethodInfo(EvaluationContext ctx, Class[] paramTypes)
+            throws ELException {
+        return this.getMethodExpression(ctx).getMethodInfo(ctx.getELContext());
+    }
+
+    private final 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..866ff2d
--- /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;
+    }
+
+    public Class getType(EvaluationContext ctx)
+            throws ELException {
+        return this.getInteger().getClass();
+    }
+
+    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..ab2461e
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstLambdaExpression.java
@@ -0,0 +1,67 @@
+/*
+ * 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.ValueExpression;
+import javax.el.LambdaExpression;
+import com.sun.el.lang.EvaluationContext;
+import com.sun.el.ValueExpressionImpl;
+import com.sun.el.util.MessageFactory;
+
+/**
+ * @author Kin-man Chung
+ */
+public
+class AstLambdaExpression extends SimpleNode {
+
+    public AstLambdaExpression(int id) {
+      super(id);
+    }
+
+    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..1573bd3
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstLambdaParameters.java
@@ -0,0 +1,41 @@
+/*
+ * 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 java.util.ArrayList;
+
+/**
+ * @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..bcc2294
--- /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);
+    }
+
+    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..4408c38
--- /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);
+    }
+
+    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..f8d8a51
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstListData.java
@@ -0,0 +1,39 @@
+/*
+ * 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);
+    }
+
+    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..168fb8f
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstLiteralExpression.java
@@ -0,0 +1,61 @@
+/*
+ * 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);
+    }
+
+    public Class getType(EvaluationContext ctx) throws ELException {
+        return String.class;
+    }
+
+    public Object getValue(EvaluationContext ctx) throws ELException {
+        return this.image;
+    }
+
+    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..e08ae32
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstMapData.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 java.util.HashSet;
+import java.util.HashMap;
+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);
+    }
+
+    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..0e9b256
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstMapEntry.java
@@ -0,0 +1,27 @@
+/*
+ * 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..c67d452
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstMethodArguments.java
@@ -0,0 +1,56 @@
+/*
+ * 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..6b5d11b
--- /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);
+    }
+
+    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..b7c0e85
--- /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);
+    }
+
+    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..a3f4265
--- /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);
+    }
+
+    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..4518150
--- /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);
+    }
+
+    public Class getType(EvaluationContext ctx)
+            throws ELException {
+        return Number.class;
+    }
+
+    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..94f3a15
--- /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);
+    }
+
+    public Class getType(EvaluationContext ctx)
+            throws ELException {
+        return Boolean.class;
+    }
+
+    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..5fa4d60
--- /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);
+    }
+
+    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..4ece392
--- /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);
+    }
+
+    public Class getType(EvaluationContext ctx)
+            throws ELException {
+        return null;
+    }
+
+    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..94d4024
--- /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);
+    }
+
+    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..5433418
--- /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);
+    }
+
+    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..b787831
--- /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);
+    }
+
+    public Object getValue(EvaluationContext ctx)
+            throws ELException {
+        this.children[0].getValue(ctx);
+        return this.children[1].getValue(ctx);
+    }
+
+    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..f533ccc
--- /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;
+    }
+
+    public Class getType(EvaluationContext ctx)
+            throws ELException {
+        return String.class;
+    }
+
+    public Object getValue(EvaluationContext ctx)
+            throws ELException {
+        return this.getString();
+    }
+
+    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..28b5584
--- /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);
+    }
+
+    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..12fc5aa
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/AstValue.java
@@ -0,0 +1,272 @@
+/*
+ * 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.ELException;
+import javax.el.ELResolver;
+import javax.el.MethodInfo;
+import javax.el.ValueReference;
+import javax.el.ELClass;
+import javax.el.PropertyNotFoundException;
+import javax.el.PropertyNotWritableException;
+import javax.el.ImportHandler;
+
+import com.sun.el.lang.EvaluationContext;
+import com.sun.el.lang.ELSupport;
+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);
+    }
+
+    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;
+    }
+
+    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 final 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 final 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]);
+    }
+
+    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;
+    }
+
+    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;
+    }
+
+    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);
+        }
+    }
+
+    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());
+    }
+
+    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..6a91a2e
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/BooleanNode.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 class BooleanNode extends SimpleNode {
+    /**
+     * @param i
+     */
+    public BooleanNode(int i) {
+        super(i);
+    }
+    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..8321548
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/ELParser.java
@@ -0,0 +1,3029 @@
+/*
+ * 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);
+          }
+    }
+  }
+
+/*
+ * Expression
+ * EL Expression Language 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..703f6ef
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/ELParser.jjt
@@ -0,0 +1,573 @@
+/*
+ * 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
+ */
+
+/*
+	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..1106e0d
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/ELParserConstants.java
@@ -0,0 +1,218 @@
+/*
+ * 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..7d04aec
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/ELParserTokenManager.java
@@ -0,0 +1,2079 @@
+/*
+ * 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. ELParserTokenManager.java */
+package com.sun.el.parser;
+import java.io.StringReader;
+import javax.el.ELException;
+
+/** 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 ((0xf7ffffffffffffffL & l) == 0L)
+                     break;
+                  if (kind > 1)
+                     kind = 1;
+                  jjCheckNAddStates(0, 3);
+                  break;
+               default : break;
+            }
+         } while(i != startsAt);
+      }
+      else
+      {
+         int hiByte = (int)(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 = (int)(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 = (int)(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..aa6def4
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/ELParserTreeConstants.java
@@ -0,0 +1,111 @@
+/*
+ * 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
+{
+  public int JJTCOMPOSITEEXPRESSION = 0;
+  public int JJTLITERALEXPRESSION = 1;
+  public int JJTDEFERREDEXPRESSION = 2;
+  public int JJTDYNAMICEXPRESSION = 3;
+  public int JJTVOID = 4;
+  public int JJTSEMICOLON = 5;
+  public int JJTASSIGN = 6;
+  public int JJTLAMBDAEXPRESSION = 7;
+  public int JJTLAMBDAPARAMETERS = 8;
+  public int JJTCHOICE = 9;
+  public int JJTOR = 10;
+  public int JJTAND = 11;
+  public int JJTEQUAL = 12;
+  public int JJTNOTEQUAL = 13;
+  public int JJTLESSTHAN = 14;
+  public int JJTGREATERTHAN = 15;
+  public int JJTLESSTHANEQUAL = 16;
+  public int JJTGREATERTHANEQUAL = 17;
+  public int JJTCONCAT = 18;
+  public int JJTPLUS = 19;
+  public int JJTMINUS = 20;
+  public int JJTMULT = 21;
+  public int JJTDIV = 22;
+  public int JJTMOD = 23;
+  public int JJTNEGATIVE = 24;
+  public int JJTNOT = 25;
+  public int JJTEMPTY = 26;
+  public int JJTVALUE = 27;
+  public int JJTDOTSUFFIX = 28;
+  public int JJTBRACKETSUFFIX = 29;
+  public int JJTMETHODARGUMENTS = 30;
+  public int JJTMAPDATA = 31;
+  public int JJTMAPENTRY = 32;
+  public int JJTLISTDATA = 33;
+  public int JJTIDENTIFIER = 34;
+  public int JJTFUNCTION = 35;
+  public int JJTTRUE = 36;
+  public int JJTFALSE = 37;
+  public int JJTFLOATINGPOINT = 38;
+  public int JJTINTEGER = 39;
+  public int JJTSTRING = 40;
+  public int JJTNULL = 41;
+
+
+  public 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..e348d83
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/JJTELParserState.java
@@ -0,0 +1,139 @@
+/*
+ * 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..a568950
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/Node.java
@@ -0,0 +1,74 @@
+/*
+ * 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. */
+  public void jjtOpen();
+
+  /** This method is called after all the child nodes have been
+    added. */
+  public void jjtClose();
+
+  /** This pair of methods are used to inform the node of its
+    parent. */
+  public void jjtSetParent(Node n);
+  public Node jjtGetParent();
+
+  /** This method tells the node to add its argument to the node's
+    list of children.  */
+  public void jjtAddChild(Node n, int i);
+
+  /** This method returns a child node.  The children are numbered
+     from zero, left to right. */
+  public Node jjtGetChild(int i);
+
+  /** Return the number of children the node has. */
+  public int jjtGetNumChildren();
+  
+  public String getImage();
+  
+  public Object getValue(EvaluationContext ctx) throws ELException;
+  public void setValue(EvaluationContext ctx, Object value) throws ELException;
+  public Class getType(EvaluationContext ctx) throws ELException;
+  public ValueReference getValueReference(EvaluationContext ctx)
+             throws ELException;
+  public boolean isReadOnly(EvaluationContext ctx) throws ELException;
+  public void accept(NodeVisitor visitor) throws ELException;
+  public MethodInfo getMethodInfo(EvaluationContext ctx, Class[] paramTypes) throws ELException;
+  public Object invoke(EvaluationContext ctx, Class[] paramTypes, Object[] paramValues) throws ELException;
+
+  public boolean equals(Object n);
+  public int hashCode();
+  public 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..d71a2ec
--- /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 {
+    public 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..70b7dd1
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/ParseException.java
@@ -0,0 +1,207 @@
+/*
+ * 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.
+   */
+  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..816e5b9
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/SimpleCharStream.java
@@ -0,0 +1,487 @@
+/*
+ * 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..8f836f4
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/SimpleNode.java
@@ -0,0 +1,220 @@
+/*
+ * 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 javax.el.PropertyNotWritableException;
+
+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;
+    }
+
+    public void jjtOpen() {
+    }
+
+    public void jjtClose() {
+    }
+
+    public void jjtSetParent(Node n) {
+        parent = n;
+    }
+
+    public Node jjtGetParent() {
+        return parent;
+    }
+
+    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;
+    }
+
+    public Node jjtGetChild(int i) {
+        return children[i];
+    }
+
+    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.
+     */
+
+    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 + " ");
+                }
+            }
+        }
+    }
+
+    public String getImage() {
+        return image;
+    }
+
+    public void setImage(String image) {
+        this.image = image;
+    }
+
+    public Class getType(EvaluationContext ctx)
+            throws ELException {
+        throw new UnsupportedOperationException();
+    }
+
+    public Object getValue(EvaluationContext ctx)
+            throws ELException {
+        throw new UnsupportedOperationException();
+    }
+
+    public ValueReference getValueReference(EvaluationContext ctx)
+            throws ELException {
+        return null;
+    }
+
+    public boolean isReadOnly(EvaluationContext ctx)
+            throws ELException {
+        return true;
+    }
+
+    public void setValue(EvaluationContext ctx, Object value)
+            throws ELException {
+        throw new PropertyNotWritableException(MessageFactory.get("error.syntax.set"));
+    }
+
+    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);
+            }
+        }
+    }
+
+    public Object invoke(EvaluationContext ctx, Class[] paramTypes, Object[] paramValues) throws ELException {
+        throw new UnsupportedOperationException();
+    }
+
+    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..98777c0
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/Token.java
@@ -0,0 +1,98 @@
+/*
+ * 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.
+   */
+  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..bcfe037
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/parser/TokenMgrError.java
@@ -0,0 +1,148 @@
+/*
+ * 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.
+    */
+   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..1cae95c
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/stream/Optional.java
@@ -0,0 +1,65 @@
+/*
+ * 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;
+import java.util.Comparator;
+
+import javax.el.ELContext;
+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..4e7f4a6
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/stream/Stream.java
@@ -0,0 +1,564 @@
+/*
+ * 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 com.sun.el.lang.ELSupport;
+import com.sun.el.lang.ELArithmetic;
+
+import java.lang.reflect.Array;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.PriorityQueue;
+import java.util.Set;
+
+import javax.el.ELException;
+import javax.el.LambdaExpression;
+
+
+/*
+ */
+
+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..bb29e83
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/stream/StreamELResolver.java
@@ -0,0 +1,129 @@
+/*
+ * 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.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.lang.reflect.Array;
+
+import javax.el.ELContext;
+import javax.el.ELException;
+import javax.el.ELResolver;
+import javax.el.LambdaExpression;
+
+/*
+ * This ELResolver intercepts method calls to a Collections, to provide
+ * support for collection operations. 
+ */
+
+public class StreamELResolver extends ELResolver {
+
+    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;
+    }
+*/
+    public Object getValue(ELContext context, Object base, Object property) {
+        return null;
+    }
+
+    public Class<?> getType(ELContext context, Object base, Object property) {
+        return null;
+    }
+
+    public void setValue(ELContext context, Object base, Object property,
+                                  Object value) {
+    }
+
+    public boolean isReadOnly(ELContext context, Object base, Object property) {
+        return false;
+    }
+
+    public Iterator<FeatureDescriptor> getFeatureDescriptors(
+                                            ELContext context,
+                                            Object base) {
+        return null;
+    }
+
+    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..293b040
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/util/MessageFactory.java
@@ -0,0 +1,70 @@
+/*
+ * 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..7ea8987
--- /dev/null
+++ b/impl/src/main/java/com/sun/el/util/ReflectionUtil.java
@@ -0,0 +1,752 @@
+/*
+ * 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.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.InvocationTargetException;
+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/nb-configuration.xml b/nb-configuration.xml
new file mode 100644
index 0000000..72bf4d8
--- /dev/null
+++ b/nb-configuration.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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
+
+-->
+
+<project-shared-configuration>
+    <!--
+This file contains additional configuration written by modules in the NetBeans IDE.
+The configuration is intended to be shared among all the users of project and
+therefore it is assumed to be part of version control checkout.
+Without this configuration present, some functionality in the IDE may be limited or fail altogether.
+-->
+    <properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
+        <!--
+Properties that influence various parts of the IDE, especially code formatting and the like. 
+You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
+That way multiple projects can share the same settings (useful for formatting rules for example).
+Any value defined here will override the pom.xml file value but is only applicable to the current project.
+-->
+        <netbeans.compile.on.save>all</netbeans.compile.on.save>
+    </properties>
+</project-shared-configuration>
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..abc1fc6
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,391 @@
+<?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">
+    
+    <parent>
+      <groupId>org.eclipse.ee4j</groupId>
+      <artifactId>project</artifactId>
+      <version>1.0</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>org.glassfish</groupId>
+    <artifactId>javax.el</artifactId>
+    <version>3.0.1-b11-SNAPSHOT</version>
+    <packaging>jar</packaging>
+    <name>Expression Language 3.0</name>
+
+    <properties>
+        <!-- the bundle build number must be the same as the maven number -->
+        <bundle.version>3.0.0</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>
+        <tlda-license.url>http://hudson-sca.us.oracle.com/job/tlda-license/lastSuccessfulBuild/artifact</tlda-license.url>
+    </properties>
+
+    <description>Expression Language (JSR 341) Reference Implementation</description>
+    <url>https://projects.eclipse.org/projects/ee4j.el</url>
+
+
+    <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>EL 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>
+        <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>
+            <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-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>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>2.8.1</version>
+                <executions>
+                    <execution>
+                        <id>attach-javadocs</id>
+                            <goals>
+                                <goal>jar</goal>
+                            </goals>
+                        <configuration>
+                            <sourcepath>api/src;impl/src</sourcepath>
+                            <groups>
+                                <group>
+                                    <title>Expresion 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>
+                <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>
+                    <!--
+                    <parallel>classes</parallel>
+                    -->
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.glassfish.copyright</groupId>
+                <artifactId>glassfish-copyright-maven-plugin</artifactId>
+                <version>1.32</version>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-remote-resources-plugin</artifactId>
+                <version>1.2.1</version>
+                <executions>
+                  <execution>
+                    <goals>
+                      <goal>process</goal>
+                    </goals>
+                    <configuration>
+                      <resourceBundles>
+                        <resourceBundle>org.glassfish:legal:1.1</resourceBundle>
+                      </resourceBundles>
+                    </configuration>
+                  </execution>
+                </executions>
+            </plugin>
+        </plugins>
+        <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>
+        </resources>
+    </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>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.12</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>licensee</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.codehaus.mojo</groupId>
+                        <artifactId>wagon-maven-plugin</artifactId>
+                        <version>1.0-beta-4</version>
+                        <inherited>false</inherited>
+                        <executions>
+                            <execution>
+                                <id>get-license</id>
+                                <phase>package</phase>
+                                <goals>
+                                    <goal>download-single</goal>
+                                </goals>
+                                <configuration>
+                                    <url>${tlda-license.url}</url>
+                                    <fromFile>
+                                        TLDA_SCSL_Licensees_License_Notice.txt
+                                    </fromFile>
+                                    <toDir>${project.build.directory}/license</toDir>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-assembly-plugin</artifactId>
+                        <version>2.4</version>
+                        <inherited>false</inherited>
+                        <executions>
+                            <execution>
+                                <id>make-licensee-src-assembly</id>
+                                <phase>package</phase>
+                                <goals>
+                                    <goal>single</goal>
+                                </goals>
+                                <configuration>
+                                    <finalName>el-${project.version}-src</finalName>
+                                    <attach>false</attach>
+                                    <appendAssemblyId>false</appendAssemblyId>
+                                    <descriptors>
+                                        <descriptor>src/assembly/assembly.xml</descriptor>
+                                    </descriptors>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+</project>
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/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..e8c5ef6
--- /dev/null
+++ b/src/test/java/org/glassfish/el/test/StreamTest.java
@@ -0,0 +1,283 @@
+/*
+ * 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