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 © 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
+ * "<init>"or "<clinit>" 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 "<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><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>
+<c:forEach var="item" items="#{model.list}">
+ <h:inputText value="#{item.name}"/>
+</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><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