Copybara import of the project: - 35f048cfae521189399a614f78a36c156f5e4c56 Initial contribution by Yamini K B <yamini.k.b@oracle.com> - 080692e83e4039403015e00c070f435b92e0aa27 Merge pull request #51 from ggam/master by Arjan Tijms <arjan.tijms@gmail.com> - 003e833ab0fd7eba1b30cb2a0fc066fc7bc3cd1f Merge pull request #53 from arjantijms/cleanup by Arjan Tijms <arjan.tijms@gmail.com> - 0334480ae40f514890ac2d48fec8c8176f35fa10 Merge pull request #54 from vinayvishal/remove-build-numb... by Arjan Tijms <arjan.tijms@gmail.com> - 72a825bedca1b11bc9bc4e4a88e8606f7bbfd1c2 Merge pull request #55 from vinayvishal/update-parent-pom... by Arjan Tijms <arjan.tijms@gmail.com> - 9147d0bcae7c850234815be4cc63c92ab8f0bd94 Merge pull request #56 from vinayvishal/add-required-plug... by Arjan Tijms <arjan.tijms@gmail.com> - 65e89aa76ed672761099ca4d2960fc6e246c0ca2 Merge pull request #59 from markt-asf/version-bump by Arjan Tijms <arjan.tijms@gmail.com> - dd440de92af3df5f3ba29fe594475671fe93c78d Merge pull request #62 from markt-asf/fix-extensionName by Arjan Tijms <arjan.tijms@gmail.com> - 56e45b79cd84d0227cd591bb49ec51aa0d0a2ead Merge pull request #60 from markt-asf/header-updates by Arjan Tijms <arjan.tijms@gmail.com> - a9f8a30143d96895ba6e653548cfcb2346aeb104 Merge pull request #66 from markt-asf/clean-up by Arjan Tijms <arjan.tijms@gmail.com> - 3ba2b58bab35c1758d81e2511c14ee36b40e1f65 Merge pull request #67 from Cousjava/updateparent by Arjan Tijms <arjan.tijms@gmail.com> - 21ff9158fb5bd21a8e74bf45554e4203689292ec Merge pull request #74 from markt-asf/master by Arjan Tijms <arjan.tijms@gmail.com> - fa0b884f1a66394c02f7610fe0a8d37ddfb5af32 Merge pull request #88 from arjantijms/master by Arjan Tijms <arjan.tijms@gmail.com> - 78b82726c2ea23186b890c1e2ece92b58624e85f Merge pull request #89 from arjantijms/master by Arjan Tijms <arjan.tijms@gmail.com> - 20965ed125f61696227064ab216d36486905f362 Merge pull request #90 from arjantijms/master by Arjan Tijms <arjan.tijms@gmail.com> - b3fcc1219c01e8404c72f579388bce3de167a6ec Merge pull request #91 from arjantijms/master by Arjan Tijms <arjan.tijms@gmail.com> - cf4da19f4f5eb441642d0c0a38dd20872d85a02d Merge pull request #92 from arjantijms/master by Arjan Tijms <arjan.tijms@gmail.com> - 50d69173777f83495dcf95b9e735f1d3355608ba Merge pull request #93 from arjantijms/master by Arjan Tijms <arjan.tijms@gmail.com> - 341b83102592b5d4a4c74c498000234e00f58cba Merge pull request #94 from arjantijms/master by Arjan Tijms <arjan.tijms@gmail.com> - 53ae0f7924140a08352401940ae9b4378871adb9 Merge pull request #95 from arjantijms/master by Arjan Tijms <arjan.tijms@gmail.com> - f9f0b3bfb4067c1db148e3dbd68da3df6615b90e Merge pull request #96 from arjantijms/master by Arjan Tijms <arjan.tijms@gmail.com> - 8bda97fefecb97eaead2b4a81968450bf1e0b34c Merge pull request #99 from scottmarlow/singnature by Arjan Tijms <arjan.tijms@gmail.com> - fcc0e1909a2fd9a84794c015237d27e85923df21 Merge pull request #100 from arjantijms/master by Arjan Tijms <arjan.tijms@gmail.com> - 04f3e5c8b6c4ea0123f868a184464ee6b87a6f5e Merge pull request #101 from arjantijms/master by Arjan Tijms <arjan.tijms@gmail.com> - 0a0b88253484cbb27d41a393f40526efd2c0c340 Merge pull request #102 from arjantijms/master by Arjan Tijms <arjan.tijms@gmail.com> - 2d97367b33159db1827c23390786b5d741dc781c Merge pull request #104 from arjantijms/master by Arjan Tijms <arjan.tijms@gmail.com> - 93021e5025868e79d606c3f4f20001db7fcff45e Prepare release org.glassfish:jakarta.el:3.0.3 by Eclipse EL-api Bot <el-bot@eclipse.org> - c76b9e688dc7731b84260400abfe8e785294d865 3x patch (#165) by Maxim Nesen <24524084+senivam@users.noreply.github.com> - 9be46eef3f619ccf380529ba12cafb9f23aea750 Prepare release org.glassfish:jakarta.el:3.0.4 by Eclipse EL Bot <el-bot@eclipse.org> GitOrigin-RevId: 9be46eef3f619ccf380529ba12cafb9f23aea750 Change-Id: Ic5da60957c82f987950263992ca3e0687e9bc8fe
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..26c150a --- /dev/null +++ b/.gitignore
@@ -0,0 +1,37 @@ +*.class +.classpath +.project + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +.idea +*.iml +target + +# emacs +*~ + +*.class +*.jar +target/ +/bin/ +/dist/ +.settings/ +/**/.classpath +/**/.project + +/.project +nb-configuration.xml + +# mac +.DS_Store +
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..602c804 --- /dev/null +++ b/CONTRIBUTING.md
@@ -0,0 +1,48 @@ +# Contributing to Jakarta Expression Language + +Thanks for your interest in this project. + +## Project description + +Jakarta Expression Language provides an important mechanism +for enabling the presentation layer (web pages) to communicate with the +application logic (managed beans). Jakarta Expression Language is used by several Jakarta EE +technologies, such as Jakarta Faces, Jakarta Server Pages, Jakarta Security and Contexts and +Dependency Injection for Jakarta EE (CDI). + +Jakarta Expression Language can also be used in stand-alone environments. + +* https://projects.eclipse.org/projects/ee4j.el + +## Developer resources + +Information regarding source code management, builds, coding standards, and +more. + +* https://projects.eclipse.org/projects/ee4j.el/developer + +The project maintains the following source code repositories + +* https://github.com/eclipse-ee4j/el-ri + +## Eclipse Contributor Agreement + +Before your contribution can be accepted by the project team contributors must +electronically sign the Eclipse Contributor Agreement (ECA). + +* http://www.eclipse.org/legal/ECA.php + +Commits that are provided by non-committers must have a Signed-off-by field in +the footer indicating that the author is aware of the terms by which the +contribution has been provided to the project. The non-committer must +additionally have an Eclipse Foundation account and must have a signed Eclipse +Contributor Agreement (ECA) on file. + +For more information, please see the Eclipse Committer Handbook: +https://www.eclipse.org/projects/handbook/#resources-commit + +## Contact + +Contact the project developers via the project's "dev" list. + +*
diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..5de3d1b --- /dev/null +++ b/LICENSE.md
@@ -0,0 +1,637 @@ +# Eclipse Public License - v 2.0 + + THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE + PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION + OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. + + 1. DEFINITIONS + + "Contribution" means: + + a) in the case of the initial Contributor, the initial content + Distributed under this Agreement, and + + b) in the case of each subsequent Contributor: + i) changes to the Program, and + ii) additions to the Program; + where such changes and/or additions to the Program originate from + and are Distributed by that particular Contributor. A Contribution + "originates" from a Contributor if it was added to the Program by + such Contributor itself or anyone acting on such Contributor's behalf. + Contributions do not include changes or additions to the Program that + are not Modified Works. + + "Contributor" means any person or entity that Distributes the Program. + + "Licensed Patents" mean patent claims licensable by a Contributor which + are necessarily infringed by the use or sale of its Contribution alone + or when combined with the Program. + + "Program" means the Contributions Distributed in accordance with this + Agreement. + + "Recipient" means anyone who receives the Program under this Agreement + or any Secondary License (as applicable), including Contributors. + + "Derivative Works" shall mean any work, whether in Source Code or other + form, that is based on (or derived from) the Program and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. + + "Modified Works" shall mean any work in Source Code or other form that + results from an addition to, deletion from, or modification of the + contents of the Program, including, for purposes of clarity any new file + in Source Code form that contains any contents of the Program. Modified + Works shall not include works that contain only declarations, + interfaces, types, classes, structures, or files of the Program solely + in each case in order to link to, bind by name, or subclass the Program + or Modified Works thereof. + + "Distribute" means the acts of a) distributing or b) making available + in any manner that enables the transfer of a copy. + + "Source Code" means the form of a Program preferred for making + modifications, including but not limited to software source code, + documentation source, and configuration files. + + "Secondary License" means either the GNU General Public License, + Version 2.0, or any later versions of that license, including any + exceptions or additional permissions as identified by the initial + Contributor. + + 2. GRANT OF RIGHTS + + a) Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free copyright + license to reproduce, prepare Derivative Works of, publicly display, + publicly perform, Distribute and sublicense the Contribution of such + Contributor, if any, and such Derivative Works. + + b) Subject to the terms of this Agreement, each Contributor hereby + grants Recipient a non-exclusive, worldwide, royalty-free patent + license under Licensed Patents to make, use, sell, offer to sell, + import and otherwise transfer the Contribution of such Contributor, + if any, in Source Code or other form. This patent license shall + apply to the combination of the Contribution and the Program if, at + the time the Contribution is added by the Contributor, such addition + of the Contribution causes such combination to be covered by the + Licensed Patents. The patent license shall not apply to any other + combinations which include the Contribution. No hardware per se is + licensed hereunder. + + c) Recipient understands that although each Contributor grants the + licenses to its Contributions set forth herein, no assurances are + provided by any Contributor that the Program does not infringe the + patent or other intellectual property rights of any other entity. + Each Contributor disclaims any liability to Recipient for claims + brought by any other entity based on infringement of intellectual + property rights or otherwise. As a condition to exercising the + rights and licenses granted hereunder, each Recipient hereby + assumes sole responsibility to secure any other intellectual + property rights needed, if any. For example, if a third party + patent license is required to allow Recipient to Distribute the + Program, it is Recipient's responsibility to acquire that license + before distributing the Program. + + d) Each Contributor represents that to its knowledge it has + sufficient copyright rights in its Contribution, if any, to grant + the copyright license set forth in this Agreement. + + e) Notwithstanding the terms of any Secondary License, no + Contributor makes additional grants to any Recipient (other than + those set forth in this Agreement) as a result of such Recipient's + receipt of the Program under the terms of a Secondary License + (if permitted under the terms of Section 3). + + 3. REQUIREMENTS + + 3.1 If a Contributor Distributes the Program in any form, then: + + a) the Program must also be made available as Source Code, in + accordance with section 3.2, and the Contributor must accompany + the Program with a statement that the Source Code for the Program + is available under this Agreement, and informs Recipients how to + obtain it in a reasonable manner on or through a medium customarily + used for software exchange; and + + b) the Contributor may Distribute the Program under a license + different than this Agreement, provided that such license: + i) effectively disclaims on behalf of all other Contributors all + warranties and conditions, express and implied, including + warranties or conditions of title and non-infringement, and + implied warranties or conditions of merchantability and fitness + for a particular purpose; + + ii) effectively excludes on behalf of all other Contributors all + liability for damages, including direct, indirect, special, + incidental and consequential damages, such as lost profits; + + iii) does not attempt to limit or alter the recipients' rights + in the Source Code under section 3.2; and + + iv) requires any subsequent distribution of the Program by any + party to be under a license that satisfies the requirements + of this section 3. + + 3.2 When the Program is Distributed as Source Code: + + a) it must be made available under this Agreement, or if the + Program (i) is combined with other material in a separate file or + files made available under a Secondary License, and (ii) the initial + Contributor attached to the Source Code the notice described in + Exhibit A of this Agreement, then the Program may be made available + under the terms of such Secondary Licenses, and + + b) a copy of this Agreement must be included with each copy of + the Program. + + 3.3 Contributors may not remove or alter any copyright, patent, + trademark, attribution notices, disclaimers of warranty, or limitations + of liability ("notices") contained within the Program from any copy of + the Program which they Distribute, provided that Contributors may add + their own appropriate notices. + + 4. COMMERCIAL DISTRIBUTION + + Commercial distributors of software may accept certain responsibilities + with respect to end users, business partners and the like. While this + license is intended to facilitate the commercial use of the Program, + the Contributor who includes the Program in a commercial product + offering should do so in a manner which does not create potential + liability for other Contributors. Therefore, if a Contributor includes + the Program in a commercial product offering, such Contributor + ("Commercial Contributor") hereby agrees to defend and indemnify every + other Contributor ("Indemnified Contributor") against any losses, + damages and costs (collectively "Losses") arising from claims, lawsuits + and other legal actions brought by a third party against the Indemnified + Contributor to the extent caused by the acts or omissions of such + Commercial Contributor in connection with its distribution of the Program + in a commercial product offering. The obligations in this section do not + apply to any claims or Losses relating to any actual or alleged + intellectual property infringement. In order to qualify, an Indemnified + Contributor must: a) promptly notify the Commercial Contributor in + writing of such claim, and b) allow the Commercial Contributor to control, + and cooperate with the Commercial Contributor in, the defense and any + related settlement negotiations. The Indemnified Contributor may + participate in any such claim at its own expense. + + For example, a Contributor might include the Program in a commercial + product offering, Product X. That Contributor is then a Commercial + Contributor. If that Commercial Contributor then makes performance + claims, or offers warranties related to Product X, those performance + claims and warranties are such Commercial Contributor's responsibility + alone. Under this section, the Commercial Contributor would have to + defend claims against the other Contributors related to those performance + claims and warranties, and if a court requires any other Contributor to + pay any damages as a result, the Commercial Contributor must pay + those damages. + + 5. NO WARRANTY + + EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT + PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS" + BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR + IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF + TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR + PURPOSE. Each Recipient is solely responsible for determining the + appropriateness of using and distributing the Program and assumes all + risks associated with its exercise of rights under this Agreement, + including but not limited to the risks and costs of program errors, + compliance with applicable laws, damage to or loss of data, programs + or equipment, and unavailability or interruption of operations. + + 6. DISCLAIMER OF LIABILITY + + EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT + PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS + SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST + PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE + EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGES. + + 7. GENERAL + + If any provision of this Agreement is invalid or unenforceable under + applicable law, it shall not affect the validity or enforceability of + the remainder of the terms of this Agreement, and without further + action by the parties hereto, such provision shall be reformed to the + minimum extent necessary to make such provision valid and enforceable. + + If Recipient institutes patent litigation against any entity + (including a cross-claim or counterclaim in a lawsuit) alleging that the + Program itself (excluding combinations of the Program with other software + or hardware) infringes such Recipient's patent(s), then such Recipient's + rights granted under Section 2(b) shall terminate as of the date such + litigation is filed. + + All Recipient's rights under this Agreement shall terminate if it + fails to comply with any of the material terms or conditions of this + Agreement and does not cure such failure in a reasonable period of + time after becoming aware of such noncompliance. If all Recipient's + rights under this Agreement terminate, Recipient agrees to cease use + and distribution of the Program as soon as reasonably practicable. + However, Recipient's obligations under this Agreement and any licenses + granted by Recipient relating to the Program shall continue and survive. + + Everyone is permitted to copy and distribute copies of this Agreement, + but in order to avoid inconsistency the Agreement is copyrighted and + may only be modified in the following manner. The Agreement Steward + reserves the right to publish new versions (including revisions) of + this Agreement from time to time. No one other than the Agreement + Steward has the right to modify this Agreement. The Eclipse Foundation + is the initial Agreement Steward. The Eclipse Foundation may assign the + responsibility to serve as the Agreement Steward to a suitable separate + entity. Each new version of the Agreement will be given a distinguishing + version number. The Program (including Contributions) may always be + Distributed subject to the version of the Agreement under which it was + received. In addition, after a new version of the Agreement is published, + Contributor may elect to Distribute the Program (including its + Contributions) under the new version. + + Except as expressly stated in Sections 2(a) and 2(b) above, Recipient + receives no rights or licenses to the intellectual property of any + Contributor under this Agreement, whether expressly, by implication, + estoppel or otherwise. All rights in the Program not expressly granted + under this Agreement are reserved. Nothing in this Agreement is intended + to be enforceable by any entity that is not a Contributor or Recipient. + No third-party beneficiary rights are created under this Agreement. + + Exhibit A - Form of Secondary Licenses Notice + + "This Source Code may also be made available under the following + Secondary Licenses when the conditions for such availability set forth + in the Eclipse Public License, v. 2.0 are satisfied: {name license(s), + version(s), and exceptions or additional permissions here}." + + Simply including a copy of this Agreement, including this Exhibit A + is not sufficient to license the Source Code under Secondary Licenses. + + If it is not possible or desirable to put the notice in a particular + file, then You may include the notice in a location (such as a LICENSE + file in a relevant directory) where a recipient would be likely to + look for such a notice. + + You may add additional accurate notices of copyright ownership. + +--- + +## The GNU General Public License (GPL) Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor + Boston, MA 02110-1335 + USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your freedom to + share and change it. By contrast, the GNU General Public License is + intended to guarantee your freedom to share and change free software--to + make sure the software is free for all its users. This General Public + License applies to most of the Free Software Foundation's software and + to any other program whose authors commit to using it. (Some other Free + Software Foundation software is covered by the GNU Library General + Public License instead.) You can apply it to your programs, too. + + When we speak of free software, we are referring to freedom, not price. + Our General Public Licenses are designed to make sure that you have the + freedom to distribute copies of free software (and charge for this + service if you wish), that you receive source code or can get it if you + want it, that you can change the software or use pieces of it in new + free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid anyone + to deny you these rights or to ask you to surrender the rights. These + restrictions translate to certain responsibilities for you if you + distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether gratis + or for a fee, you must give the recipients all the rights that you have. + You must make sure that they, too, receive or can get the source code. + And you must show them these terms so they know their rights. + + We protect your rights with two steps: (1) copyright the software, and + (2) offer you this license which gives you legal permission to copy, + distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain + that everyone understands that there is no warranty for this free + software. If the software is modified by someone else and passed on, we + want its recipients to know that what they have is not the original, so + that any problems introduced by others will not reflect on the original + authors' reputations. + + Finally, any free program is threatened constantly by software patents. + We wish to avoid the danger that redistributors of a free program will + individually obtain patent licenses, in effect making the program + proprietary. To prevent this, we have made it clear that any patent must + be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and + modification follow. + + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains a + notice placed by the copyright holder saying it may be distributed under + the terms of this General Public License. The "Program", below, refers + to any such program or work, and a "work based on the Program" means + either the Program or any derivative work under copyright law: that is + to say, a work containing the Program or a portion of it, either + verbatim or with modifications and/or translated into another language. + (Hereinafter, translation is included without limitation in the term + "modification".) Each licensee is addressed as "you". + + Activities other than copying, distribution and modification are not + covered by this License; they are outside its scope. The act of running + the Program is not restricted, and the output from the Program is + covered only if its contents constitute a work based on the Program + (independent of having been made by running the Program). Whether that + is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's source + code as you receive it, in any medium, provided that you conspicuously + and appropriately publish on each copy an appropriate copyright notice + and disclaimer of warranty; keep intact all the notices that refer to + this License and to the absence of any warranty; and give any other + recipients of the Program a copy of this License along with the Program. + + You may charge a fee for the physical act of transferring a copy, and + you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion of + it, thus forming a work based on the Program, and copy and distribute + such modifications or work under the terms of Section 1 above, provided + that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any part + thereof, to be licensed as a whole at no charge to all third parties + under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a notice + that there is no warranty (or else, saying that you provide a + warranty) and that users may redistribute the program under these + conditions, and telling the user how to view a copy of this License. + (Exception: if the Program itself is interactive but does not + normally print such an announcement, your work based on the Program + is not required to print an announcement.) + + These requirements apply to the modified work as a whole. If + identifiable sections of that work are not derived from the Program, and + can be reasonably considered independent and separate works in + themselves, then this License, and its terms, do not apply to those + sections when you distribute them as separate works. But when you + distribute the same sections as part of a whole which is a work based on + the Program, the distribution of the whole must be on the terms of this + License, whose permissions for other licensees extend to the entire + whole, and thus to each and every part regardless of who wrote it. + + Thus, it is not the intent of this section to claim rights or contest + your rights to work written entirely by you; rather, the intent is to + exercise the right to control the distribution of derivative or + collective works based on the Program. + + In addition, mere aggregation of another work not based on the Program + with the Program (or with a work based on the Program) on a volume of a + storage or distribution medium does not bring the other work under the + scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, + under Section 2) in object code or executable form under the terms of + Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections 1 + and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your cost + of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed + only for noncommercial distribution and only if you received the + program in object code or executable form with such an offer, in + accord with Subsection b above.) + + The source code for a work means the preferred form of the work for + making modifications to it. For an executable work, complete source code + means all the source code for all modules it contains, plus any + associated interface definition files, plus the scripts used to control + compilation and installation of the executable. However, as a special + exception, the source code distributed need not include anything that is + normally distributed (in either source or binary form) with the major + components (compiler, kernel, and so on) of the operating system on + which the executable runs, unless that component itself accompanies the + executable. + + If distribution of executable or object code is made by offering access + to copy from a designated place, then offering equivalent access to copy + the source code from the same place counts as distribution of the source + code, even though third parties are not compelled to copy the source + along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program + except as expressly provided under this License. Any attempt otherwise + to copy, modify, sublicense or distribute the Program is void, and will + automatically terminate your rights under this License. However, parties + who have received copies, or rights, from you under this License will + not have their licenses terminated so long as such parties remain in + full compliance. + + 5. You are not required to accept this License, since you have not + signed it. However, nothing else grants you permission to modify or + distribute the Program or its derivative works. These actions are + prohibited by law if you do not accept this License. Therefore, by + modifying or distributing the Program (or any work based on the + Program), you indicate your acceptance of this License to do so, and all + its terms and conditions for copying, distributing or modifying the + Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the + Program), the recipient automatically receives a license from the + original licensor to copy, distribute or modify the Program subject to + these terms and conditions. You may not impose any further restrictions + on the recipients' exercise of the rights granted herein. You are not + responsible for enforcing compliance by third parties to this License. + + 7. If, as a consequence of a court judgment or allegation of patent + infringement or for any other reason (not limited to patent issues), + conditions are imposed on you (whether by court order, agreement or + otherwise) that contradict the conditions of this License, they do not + excuse you from the conditions of this License. If you cannot distribute + so as to satisfy simultaneously your obligations under this License and + any other pertinent obligations, then as a consequence you may not + distribute the Program at all. For example, if a patent license would + not permit royalty-free redistribution of the Program by all those who + receive copies directly or indirectly through you, then the only way you + could satisfy both it and this License would be to refrain entirely from + distribution of the Program. + + If any portion of this section is held invalid or unenforceable under + any particular circumstance, the balance of the section is intended to + apply and the section as a whole is intended to apply in other + circumstances. + + It is not the purpose of this section to induce you to infringe any + patents or other property right claims or to contest validity of any + such claims; this section has the sole purpose of protecting the + integrity of the free software distribution system, which is implemented + by public license practices. Many people have made generous + contributions to the wide range of software distributed through that + system in reliance on consistent application of that system; it is up to + the author/donor to decide if he or she is willing to distribute + software through any other system and a licensee cannot impose that choice. + + This section is intended to make thoroughly clear what is believed to be + a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in + certain countries either by patents or by copyrighted interfaces, the + original copyright holder who places the Program under this License may + add an explicit geographical distribution limitation excluding those + countries, so that distribution is permitted only in or among countries + not thus excluded. In such case, this License incorporates the + limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new + versions of the General Public License from time to time. Such new + versions will be similar in spirit to the present version, but may + differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the Program + specifies a version number of this License which applies to it and "any + later version", you have the option of following the terms and + conditions either of that version or of any later version published by + the Free Software Foundation. If the Program does not specify a version + number of this License, you may choose any version ever published by the + Free Software Foundation. + + 10. If you wish to incorporate parts of the Program into other free + programs whose distribution conditions are different, write to the + author to ask for permission. For software which is copyrighted by the + Free Software Foundation, write to the Free Software Foundation; we + sometimes make exceptions for this. Our decision will be guided by the + two goals of preserving the free status of all derivatives of our free + software and of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO + WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. + EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR + OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, + EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE + ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH + YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL + NECESSARY SERVICING, REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN + WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY + AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR + DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL + DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM + (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED + INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF + THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR + OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest + possible use to the public, the best way to achieve this is to make it + free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest to + attach them to the start of each source file to most effectively convey + the exclusion of warranty; and each file should have at least the + "copyright" line and a pointer to where the full notice is found. + + One line to give the program's name and a brief idea of what it does. + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA + + Also add information on how to contact you by electronic and paper mail. + + If the program is interactive, make it output a short notice like this + when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type + `show w'. This is free software, and you are welcome to redistribute + it under certain conditions; type `show c' for details. + + The hypothetical commands `show w' and `show c' should show the + appropriate parts of the General Public License. Of course, the commands + you use may be called something other than `show w' and `show c'; they + could even be mouse-clicks or menu items--whatever suits your program. + + You should also get your employer (if you work as a programmer) or your + school, if any, to sign a "copyright disclaimer" for the program, if + necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + program `Gnomovision' (which makes passes at compilers) written by + James Hacker. + + signature of Ty Coon, 1 April 1989 + Ty Coon, President of Vice + + This General Public License does not permit incorporating your program + into proprietary programs. If your program is a subroutine library, you + may consider it more useful to permit linking proprietary applications + with the library. If this is what you want to do, use the GNU Library + General Public License instead of this License. + +--- + +## CLASSPATH EXCEPTION + + Linking this library statically or dynamically with other modules is + making a combined work based on this library. Thus, the terms and + conditions of the GNU General Public License version 2 cover the whole + combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent + modules, and to copy and distribute the resulting executable under + terms of your choice, provided that you also meet, for each linked + independent module, the terms and conditions of the license of that + module. An independent module is a module which is not derived from or + based on this library. If you modify this library, you may extend this + exception to your version of the library, but you are not obligated to + do so. If you do not wish to do so, delete this exception statement + from your version.
diff --git a/NOTICE.md b/NOTICE.md new file mode 100644 index 0000000..9e121fd --- /dev/null +++ b/NOTICE.md
@@ -0,0 +1,45 @@ +# Notices for Jakarta Expression Language + +This content is produced and maintained by the Jakarta Expression Language project. + +* Project home: https://projects.eclipse.org/projects/ee4j.el + +## Trademarks + +Jakarta Expression Language is a trademark of the Eclipse +Foundation. + +## Copyright + +All content is the property of the respective authors or their employers. For +more information regarding authorship of content, please consult the listed +source code repository logs. + +## Declared Project Licenses + +This program and the accompanying materials are made available under the terms +of the Eclipse Public License v. 2.0 which is available at +http://www.eclipse.org/legal/epl-2.0. This Source Code may also be made +available under the following Secondary Licenses when the conditions for such +availability set forth in the Eclipse Public License v. 2.0 are satisfied: GNU +General Public License, version 2 with the GNU Classpath Exception which is +available at https://www.gnu.org/software/classpath/license.html. + +SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +## Source Code + +The project maintains the following source code repositories: + +* https://github.com/eclipse-ee4j/el-ri + +## Third-party Content + +## Cryptography + +Content may contain encryption software. The country in which you are currently +may have restrictions on the import, possession, and use, and/or re-export to +another country, of encryption software. BEFORE using any encryption software, +please check the country's laws, regulations and policies concerning the import, +possession, or use, and re-export of encryption software, to see if this is +permitted.
diff --git a/README.md b/README.md new file mode 100644 index 0000000..8e1b614 --- /dev/null +++ b/README.md
@@ -0,0 +1,24 @@ +# Jakarta Expression Language + +This repository contains the code for Jakarta Expression Language. + +[Online JavaDoc](https://javadoc.io/doc/jakarta.el/jakarta.el-api/) + +Building +-------- + +Jakarta Authentication can be built by executing the following from the project root: + +``mvn clean package`` + +The API jar can then be found in /target. + +Making Changes +-------------- + +To make changes, fork this repository, make your changes, and submit a pull request. + +About Jakarta Expression Language +------------- + +Jakarta Expression Language defines an expression language for Java applications. \ No newline at end of file
diff --git a/api/pom.xml b/api/pom.xml new file mode 100644 index 0000000..e713eaa --- /dev/null +++ b/api/pom.xml
@@ -0,0 +1,286 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Copyright (c) 1997, 2018 Oracle and/or its affiliates and others. + All rights reserved. + + This program and the accompanying materials are made available under the + terms of the Eclipse Public License v. 2.0, which is available at + http://www.eclipse.org/legal/epl-2.0. + + This Source Code may also be made available under the following Secondary + Licenses when the conditions for such availability set forth in the + Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + version 2 with the GNU Classpath Exception, which is available at + https://www.gnu.org/software/classpath/license.html. + + SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.eclipse.ee4j</groupId> + <artifactId>project</artifactId> + <version>1.0.5</version> + <relativePath/> + </parent> + + <groupId>jakarta.el</groupId> + <artifactId>jakarta.el-api</artifactId> + <version>3.0.3-SNAPSHOT</version> + + <name>Jakarta Expression Language 3.0 API</name> + <description> + Jakarta Expression Language defines an expression language for Java applications + </description> + <url>https://projects.eclipse.org/projects/ee4j.el</url> + + <properties> + <!-- Make sure the two versions are in sync with the maven version --> + <spec.version>3.0</spec.version> + <bundle.version>3.0.3</bundle.version> + <extensionName>javax.el</extensionName> + <bundle.symbolicName>javax.el-api</bundle.symbolicName> + <vendorName>Oracle Corporation</vendorName> + <findbugs.version>2.4.0</findbugs.version> + <findbugs.exclude /> + <findbugs.threshold>High</findbugs.threshold> + </properties> + + <licenses> + <license> + <name>EPL 2.0</name> + <url>http://www.eclipse.org/legal/epl-2.0</url> + <distribution>repo</distribution> + </license> + <license> + <name>GPL2 w/ CPE</name> + <url>https://www.gnu.org/software/classpath/license.html</url> + <distribution>repo</distribution> + </license> + </licenses> + + <issueManagement> + <system>github</system> + <url>https://github.com/eclipse-ee4j/el-ri/issues</url> + </issueManagement> + + <mailingLists> + <mailingList> + <name>Jakarta Expression Language mailing list</name> + <post>el-dev@eclipse.org</post> + <subscribe>https://dev.eclipse.org/mailman/listinfo/el-dev</subscribe> + <unsubscribe>https://dev.eclipse.org/mailman/listinfo/el-dev</unsubscribe> + <archive>https://dev.eclipse.org/mhonarc/lists/el-dev</archive> + </mailingList> + </mailingLists> + + <scm> + <connection>scm:git:https://github.com/eclipse-ee4j/el-ri.git</connection> + <developerConnection>scm:git:git@github.com:eclipse-ee4j/el-ri.git</developerConnection> + <url>https://github.com/eclipse-ee4j/el-ri</url> + <tag>HEAD</tag> + </scm> + + <developers> + <developer> + <id>yaminikb</id> + <name>Yamini K B</name> + <organization>Oracle Corporation</organization> + <organizationUrl>http://www.oracle.com/</organizationUrl> + </developer> + </developers> + + <contributors> + <contributor> + <name>Kin-man Chung</name> + </contributor> + </contributors> + + <build> + <resources> + <resource> + <directory>src/main/java</directory> + <includes> + <include>**/*.properties</include> + </includes> + </resource> + <resource> + <directory>${project.basedir}/..</directory> + <includes> + <include>LICENSE.md</include> + <include>NOTICE.md</include> + </includes> + <targetPath>META-INF</targetPath> + </resource> + </resources> + + <plugins> + <!-- Restricts the Java version to 1.7 --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.0</version> + <configuration> + <source>1.7</source> + <target>1.7</target> + <compilerArgument>-Xlint:unchecked</compilerArgument> + </configuration> + </plugin> + + + <!-- Creates the OSGi MANIFEST.MF file --> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <version>1.4.3</version> + <configuration> + <supportedProjectTypes> + <supportedProjectType>jar</supportedProjectType> + </supportedProjectTypes> + <instructions> + <Bundle-Description>Jakarta Expression Language ${spec.version}</Bundle-Description> + <Bundle-SymbolicName>${bundle.symbolicName}</Bundle-SymbolicName> + <Bundle-Version>${bundle.version}</Bundle-Version> + <Extension-Name>${extensionName}</Extension-Name> + <Specification-Version>${spec.version}</Specification-Version> + <Specification-Vendor>${vendorName}</Specification-Vendor> + <Implementation-Version>${project.version}</Implementation-Version> + <Implementation-Vendor>${vendorName}</Implementation-Vendor> + <Export-Package>javax.el</Export-Package> + </instructions> + </configuration> + <executions> + <execution> + <id>bundle-manifest</id> + <phase>process-classes</phase> + <goals> + <goal>manifest</goal> + </goals> + </execution> + </executions> + </plugin> + + <!-- Adds the manifest file created by the org.apache.felix:maven-bundle-plugin --> + <plugin> + <artifactId>maven-jar-plugin</artifactId> + <version>2.4</version> + <configuration> + <archive> + <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile> + </archive> + <excludes> + <exclude>**/*.java</exclude> + </excludes> + </configuration> + </plugin> + + <!-- Creates the source jar --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-source-plugin</artifactId> + <version>3.0.1</version> + <configuration> + <includePom>true</includePom> + </configuration> + <executions> + <execution> + <id>attach-sources</id> + <goals> + <goal>jar-no-fork</goal> + </goals> + </execution> + </executions> + </plugin> + + <!-- + Create Javadoc for API jar + --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-javadoc-plugin</artifactId> + <version>3.1.1</version> + <executions> + <execution> + <id>attach-api-javadocs</id> + <goals> + <goal>jar</goal> + </goals> + <configuration> + <source>1.8</source> + <additionalJOption>-Xdoclint:none</additionalJOption> + <description>Jakarta Expression Language API documentation</description> + <doctitle>Jakarta Expression Language API documentation</doctitle> + <windowtitle>Jakarta Expression Language API documentation</windowtitle> + <header><![CDATA[<br>Jakarta Expression Language API v${project.version}]]></header> + <bottom><![CDATA[ +Comments to: <a href="mailto:el-dev@eclipse.org">el-dev@eclipse.org</a>.<br> +Copyright © 2019 Eclipse Foundation. All rights reserved.<br> +Use is subject to <a href="{@docRoot}/doc-files/speclicense.html" target="_top">license terms</a>.]]> + </bottom> + <docfilessubdirs>true</docfilessubdirs> + <groups> + <group> + <title>Jakarta Expression Language API Documentation</title> + <packages> + javax.el.* + </packages> + </group> + </groups> + </configuration> + </execution> + </executions> + </plugin> + + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>findbugs-maven-plugin</artifactId> + <version>${findbugs.version}</version> + <configuration> + <threshold>${findbugs.threshold}</threshold> + <excludeFilterFile>${findbugs.exclude}</excludeFilterFile> + <findbugsXmlOutput>true</findbugsXmlOutput> + <findbugsXmlWithMessages>true</findbugsXmlWithMessages> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-release-plugin</artifactId> + <configuration> + <mavenExecutorId>forked-path</mavenExecutorId> + <useReleaseProfile>false</useReleaseProfile> + <arguments>${release.arguments}</arguments> + </configuration> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-enforcer-plugin</artifactId> + <version>3.0.0-M2</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-gpg-plugin</artifactId> + <version>1.6</version> + </plugin> + </plugins> + </build> + + <reporting> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>findbugs-maven-plugin</artifactId> + <version>${findbugs.version}</version> + <configuration> + <threshold>${findbugs.threshold}</threshold> + <excludeFilterFile>${findbugs.exclude}</excludeFilterFile> + </configuration> + </plugin> + </plugins> + </reporting> +</project>
diff --git a/api/src/main/java/javax/el/ArrayELResolver.java b/api/src/main/java/javax/el/ArrayELResolver.java new file mode 100644 index 0000000..aa088f5 --- /dev/null +++ b/api/src/main/java/javax/el/ArrayELResolver.java
@@ -0,0 +1,304 @@ +/* + * Copyright (c) 1997-2018 Oracle and/or its affiliates and others. + * All rights reserved. + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.el; + +import java.beans.FeatureDescriptor; +import java.lang.reflect.Array; +import java.util.Iterator; + +/** + * Defines property resolution behavior on arrays. + * + * <p> + * This resolver handles base objects that are Java language arrays. It accepts any object as a property and coerces + * that object into an integer index into the array. The resulting value is the value in the array at that index. + * </p> + * + * <p> + * This resolver can be constructed in read-only mode, which means that {@link #isReadOnly} will always return + * <code>true</code> and {@link #setValue} will always throw <code>PropertyNotWritableException</code>. + * </p> + * + * <p> + * <code>ELResolver</code>s are combined together using {@link CompositeELResolver}s, to define rich semantics for + * evaluating an expression. See the javadocs for {@link ELResolver} for details. + * </p> + * + * @see CompositeELResolver + * @see ELResolver + * + * @since Jakarta Server Pages 2.1 + */ +public class ArrayELResolver extends ELResolver { + + /** + * Creates a new read/write <code>ArrayELResolver</code>. + */ + public ArrayELResolver() { + this.isReadOnly = false; + } + + /** + * Creates a new <code>ArrayELResolver</code> whose read-only status is determined by the given parameter. + * + * @param isReadOnly <code>true</code> if this resolver cannot modify arrays; <code>false</code> otherwise. + */ + public ArrayELResolver(boolean isReadOnly) { + this.isReadOnly = isReadOnly; + } + + /** + * If the base object is an array, returns the most general acceptable type for a value in this array. + * + * <p> + * If the base is a <code>array</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object + * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after + * this method is called, the caller should ignore the return value. + * </p> + * + * <p> + * Assuming the base is an <code>array</code>, this method will always return + * <code>base.getClass().getComponentType()</code>, which is the most general type of component that can be stored at + * any given index in the array. + * </p> + * + * @param context The context of this evaluation. + * @param base The array to analyze. Only bases that are Java language arrays are handled by this resolver. + * @param property The index of the element in the array to return the acceptable type for. Will be coerced into an + * integer, but otherwise ignored by this resolver. + * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then + * the most general acceptable type; otherwise undefined. + * @throws PropertyNotFoundException if the given index is out of bounds for this array. + * @throws NullPointerException if context is <code>null</code> + * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown + * exception must be included as the cause property of this exception, if available. + */ + @Override + public Class<?> getType(ELContext context, Object base, Object property) { + + if (context == null) { + throw new NullPointerException(); + } + + if (base != null && base.getClass().isArray()) { + context.setPropertyResolved(true); + int index = toInteger(property); + if (index < 0 || index >= Array.getLength(base)) { + throw new PropertyNotFoundException(); + } + return base.getClass().getComponentType(); + } + return null; + } + + /** + * If the base object is a Java language array, returns the value at the given index. The index is specified by the + * <code>property</code> argument, and coerced into an integer. If the coercion could not be performed, an + * <code>IllegalArgumentException</code> is thrown. If the index is out of bounds, <code>null</code> is returned. + * + * <p> + * If the base is a Java language array, the <code>propertyResolved</code> property of the <code>ELContext</code> object + * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after + * this method is called, the caller should ignore the return value. + * </p> + * + * @param context The context of this evaluation. + * @param base The array to analyze. Only bases that are Java language arrays are handled by this resolver. + * @param property The index of the value to be returned. Will be coerced into an integer. + * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then + * the value at the given index or <code>null</code> if the index was out of bounds. Otherwise, undefined. + * @throws IllegalArgumentException if the property could not be coerced into an integer. + * @throws NullPointerException if context is <code>null</code>. + * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown + * exception must be included as the cause property of this exception, if available. + */ + @Override + public Object getValue(ELContext context, Object base, Object property) { + + if (context == null) { + throw new NullPointerException(); + } + + if (base != null && base.getClass().isArray()) { + context.setPropertyResolved(base, property); + int index = toInteger(property); + if (index >= 0 && index < Array.getLength(base)) { + return Array.get(base, index); + } + } + return null; + } + + /** + * If the base object is a Java language array, attempts to set the value at the given index with the given value. The + * index is specified by the <code>property</code> argument, and coerced into an integer. If the coercion could not be + * performed, an <code>IllegalArgumentException</code> is thrown. If the index is out of bounds, a + * <code>PropertyNotFoundException</code> is thrown. + * + * <p> + * If the base is a Java language array, the <code>propertyResolved</code> property of the <code>ELContext</code> object + * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after + * this method is called, the caller can safely assume no value was set. + * </p> + * + * <p> + * If this resolver was constructed in read-only mode, this method will always throw + * <code>PropertyNotWritableException</code>. + * </p> + * + * @param context The context of this evaluation. + * @param base The array to be modified. Only bases that are Java language arrays are handled by this resolver. + * @param property The index of the value to be set. Will be coerced into an integer. + * @param val The value to be set at the given index. + * @throws ClassCastException if the class of the specified element prevents it from being added to this array. + * @throws NullPointerException if context is <code>null</code>. + * @throws IllegalArgumentException if the property could not be coerced into an integer, or if some aspect of the + * specified element prevents it from being added to this array. + * @throws PropertyNotWritableException if this resolver was constructed in read-only mode. + * @throws PropertyNotFoundException if the given index is out of bounds for this array. + * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown + * exception must be included as the cause property of this exception, if available. + */ + @Override + public void setValue(ELContext context, Object base, Object property, Object val) { + + if (context == null) { + throw new NullPointerException(); + } + + if (base != null && base.getClass().isArray()) { + context.setPropertyResolved(base, property); + if (isReadOnly) { + throw new PropertyNotWritableException(); + } + Class<?> type = base.getClass().getComponentType(); + if (val != null && !type.isAssignableFrom(val.getClass())) { + throw new ClassCastException(); + } + int index = toInteger(property); + if (index < 0 || index >= Array.getLength(base)) { + throw new PropertyNotFoundException(); + } + Array.set(base, index, val); + } + } + + /** + * If the base object is a Java language array, returns whether a call to {@link #setValue} will always fail. + * + * <p> + * If the base is a Java language array, the <code>propertyResolved</code> property of the <code>ELContext</code> object + * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after + * this method is called, the caller should ignore the return value. + * </p> + * + * <p> + * If this resolver was constructed in read-only mode, this method will always return <code>true</code>. Otherwise, it + * returns <code>false</code>. + * </p> + * + * @param context The context of this evaluation. + * @param base The array to analyze. Only bases that are a Java language array are handled by this resolver. + * @param property The index of the element in the array to return the acceptable type for. Will be coerced into an + * integer, but otherwise ignored by this resolver. + * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then + * <code>true</code> if calling the <code>setValue</code> method will always fail or <code>false</code> if it is + * possible that such a call may succeed; otherwise undefined. + * @throws PropertyNotFoundException if the given index is out of bounds for this array. + * @throws NullPointerException if context is <code>null</code> + * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown + * exception must be included as the cause property of this exception, if available. + */ + @Override + public boolean isReadOnly(ELContext context, Object base, Object property) { + + if (context == null) { + throw new NullPointerException(); + } + + if (base != null && base.getClass().isArray()) { + context.setPropertyResolved(true); + int index = toInteger(property); + if (index < 0 || index >= Array.getLength(base)) { + throw new PropertyNotFoundException(); + } + } + return isReadOnly; + } + + /** + * Always returns <code>null</code>, since there is no reason to iterate through set set of all integers. + * + * <p> + * The {@link #getCommonPropertyType} method returns sufficient information about what properties this resolver accepts. + * </p> + * + * @param context The context of this evaluation. + * @param base The array to analyze. Only bases that are a Java language array are handled by this resolver. + * @return <code>null</code>. + */ + @Override + public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) { + return null; + } + + /** + * If the base object is a Java language array, returns the most general type that this resolver accepts for the + * <code>property</code> argument. Otherwise, returns <code>null</code>. + * + * <p> + * Assuming the base is an array, this method will always return <code>Integer.class</code>. This is because arrays + * accept integers for their index. + * </p> + * + * @param context The context of this evaluation. + * @param base The array to analyze. Only bases that are a Java language array are handled by this resolver. + * @return <code>null</code> if base is not a Java language array; otherwise <code>Integer.class</code>. + */ + @Override + public Class<?> getCommonPropertyType(ELContext context, Object base) { + + if (base != null && base.getClass().isArray()) { + return Integer.class; + } + return null; + } + + private int toInteger(Object p) { + + if (p instanceof Integer) { + return ((Integer) p).intValue(); + } + if (p instanceof Character) { + return ((Character) p).charValue(); + } + if (p instanceof Boolean) { + return ((Boolean) p).booleanValue() ? 1 : 0; + } + if (p instanceof Number) { + return ((Number) p).intValue(); + } + if (p instanceof String) { + return Integer.parseInt((String) p); + } + throw new IllegalArgumentException(); + } + + private boolean isReadOnly; +}
diff --git a/api/src/main/java/javax/el/BeanELResolver.java b/api/src/main/java/javax/el/BeanELResolver.java new file mode 100644 index 0000000..2ffd50d --- /dev/null +++ b/api/src/main/java/javax/el/BeanELResolver.java
@@ -0,0 +1,581 @@ +/* + * Copyright (c) 1997-2018 Oracle and/or its affiliates and others. + * All rights reserved. + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.el; + +import static java.lang.Boolean.TRUE; +import static javax.el.ELUtil.getExceptionMessageString; + +import java.beans.BeanInfo; +import java.beans.FeatureDescriptor; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Defines property resolution behavior on objects using the JavaBeans component architecture. + * + * <p> + * This resolver handles base objects of any type, as long as the base is not <code>null</code>. It accepts any object + * as a property or method, and coerces it to a string. + * + * <p> + * For property resolution, the property string is used to find a JavaBeans compliant property on the base object. The + * value is accessed using JavaBeans getters and setters. + * </p> + * + * <p> + * For method resolution, the method string is the name of the method in the bean. The parameter types can be optionally + * specified to identify the method. If the parameter types are not specified, the parameter objects are used in the + * method resolution. + * </p> + * + * <p> + * This resolver can be constructed in read-only mode, which means that {@link #isReadOnly} will always return + * <code>true</code> and {@link #setValue} will always throw <code>PropertyNotWritableException</code>. + * </p> + * + * <p> + * <code>ELResolver</code>s are combined together using {@link CompositeELResolver}s, to define rich semantics for + * evaluating an expression. See the javadocs for {@link ELResolver} for details. + * </p> + * + * <p> + * Because this resolver handles base objects of any type, it should be placed near the end of a composite resolver. + * Otherwise, it will claim to have resolved a property before any resolvers that come after it get a chance to test if + * they can do so as well. + * </p> + * + * @see CompositeELResolver + * @see ELResolver + * + * @since Jakarta Server Pages 2.1 + */ +public class BeanELResolver extends ELResolver { + + static private class BPSoftReference extends SoftReference<BeanProperties> { + final Class<?> key; + + BPSoftReference(Class<?> key, BeanProperties beanProperties, ReferenceQueue<BeanProperties> refQ) { + super(beanProperties, refQ); + this.key = key; + } + } + + static private class SoftConcurrentHashMap extends ConcurrentHashMap<Class<?>, BeanProperties> { + + private static final long serialVersionUID = -178867497897782229L; + private static final int CACHE_INIT_SIZE = 1024; + private ConcurrentHashMap<Class<?>, BPSoftReference> map = new ConcurrentHashMap<>(CACHE_INIT_SIZE); + private ReferenceQueue<BeanProperties> refQ = new ReferenceQueue<>(); + + // Remove map entries that have been placed on the queue by GC. + private void cleanup() { + BPSoftReference BPRef = null; + while ((BPRef = (BPSoftReference) refQ.poll()) != null) { + map.remove(BPRef.key); + } + } + + @Override + public BeanProperties put(Class<?> key, BeanProperties value) { + cleanup(); + BPSoftReference prev = map.put(key, new BPSoftReference(key, value, refQ)); + return prev == null ? null : prev.get(); + } + + @Override + public BeanProperties putIfAbsent(Class<?> key, BeanProperties value) { + cleanup(); + BPSoftReference prev = map.putIfAbsent(key, new BPSoftReference(key, value, refQ)); + return prev == null ? null : prev.get(); + } + + @Override + public BeanProperties get(Object key) { + cleanup(); + BPSoftReference BPRef = map.get(key); + if (BPRef == null) { + return null; + } + if (BPRef.get() == null) { + // value has been garbage collected, remove entry in map + map.remove(key); + return null; + } + return BPRef.get(); + } + } + + private boolean isReadOnly; + + private static final SoftConcurrentHashMap properties = new SoftConcurrentHashMap(); + + /* + * Defines a property for a bean. + */ + final static class BeanProperty { + + private Method readMethod; + private Method writeMethod; + private PropertyDescriptor descriptor; + + public BeanProperty(Class<?> baseClass, PropertyDescriptor descriptor) { + this.descriptor = descriptor; + readMethod = ELUtil.getMethod(baseClass, descriptor.getReadMethod()); + writeMethod = ELUtil.getMethod(baseClass, descriptor.getWriteMethod()); + } + + public Class<?> getPropertyType() { + return descriptor.getPropertyType(); + } + + public boolean isReadOnly() { + return getWriteMethod() == null; + } + + public Method getReadMethod() { + return readMethod; + } + + public Method getWriteMethod() { + return writeMethod; + } + } + + /* + * Defines the properties for a bean. + */ + final static class BeanProperties { + + private final Map<String, BeanProperty> propertyMap = new HashMap<>(); + + public BeanProperties(Class<?> baseClass) { + PropertyDescriptor[] descriptors; + try { + BeanInfo info = Introspector.getBeanInfo(baseClass); + descriptors = info.getPropertyDescriptors(); + } catch (IntrospectionException ie) { + throw new ELException(ie); + } + + for (PropertyDescriptor descriptor : descriptors) { + propertyMap.put(descriptor.getName(), new BeanProperty(baseClass, descriptor)); + } + } + + public BeanProperty getBeanProperty(String property) { + return propertyMap.get(property); + } + } + + /** + * Creates a new read/write <code>BeanELResolver</code>. + */ + public BeanELResolver() { + this.isReadOnly = false; + } + + /** + * Creates a new <code>BeanELResolver</code> whose read-only status is determined by the given parameter. + * + * @param isReadOnly <code>true</code> if this resolver cannot modify beans; <code>false</code> otherwise. + */ + public BeanELResolver(boolean isReadOnly) { + this.isReadOnly = isReadOnly; + } + + /** + * If the base object is not <code>null</code>, returns the most general acceptable type that can be set on this bean + * property. + * + * <p> + * If the base is not <code>null</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object + * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after + * this method is called, the caller should ignore the return value. + * </p> + * + * <p> + * The provided property will first be coerced to a <code>String</code>. If there is a <code>BeanInfoProperty</code> for + * this property and there were no errors retrieving it, the <code>propertyType</code> of the + * <code>propertyDescriptor</code> is returned. Otherwise, a <code>PropertyNotFoundException</code> is thrown. + * </p> + * + * @param context The context of this evaluation. + * @param base The bean to analyze. + * @param property The name of the property to analyze. Will be coerced to a <code>String</code>. + * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then + * the most general acceptable type; otherwise undefined. + * @throws NullPointerException if context is <code>null</code> + * @throws PropertyNotFoundException if <code>base</code> is not <code>null</code> and the specified property does not + * exist or is not readable. + * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown + * exception must be included as the cause property of this exception, if available. + */ + @Override + public Class<?> getType(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException(); + } + + if (base == null || property == null) { + return null; + } + + BeanProperty beanProperty = getBeanProperty(context, base, property); + context.setPropertyResolved(true); + return beanProperty.getPropertyType(); + } + + /** + * If the base object is not <code>null</code>, returns the current value of the given property on this bean. + * + * <p> + * If the base is not <code>null</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object + * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after + * this method is called, the caller should ignore the return value. + * </p> + * + * <p> + * The provided property name will first be coerced to a <code>String</code>. If the property is a readable property of + * the base object, as per the JavaBeans specification, then return the result of the getter call. If the getter throws + * an exception, it is propagated to the caller. If the property is not found or is not readable, a + * <code>PropertyNotFoundException</code> is thrown. + * </p> + * + * @param context The context of this evaluation. + * @param base The bean on which to get the property. + * @param property The name of the property to get. Will be coerced to a <code>String</code>. + * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then + * the value of the given property. Otherwise, undefined. + * @throws NullPointerException if context is <code>null</code>. + * @throws PropertyNotFoundException if <code>base</code> is not <code>null</code> and the specified property does not + * exist or is not readable. + * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown + * exception must be included as the cause property of this exception, if available. + */ + @Override + public Object getValue(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException(); + } + + if (base == null || property == null) { + return null; + } + + Method method = getBeanProperty(context, base, property).getReadMethod(); + if (method == null) { + throw new PropertyNotFoundException( + getExceptionMessageString(context, "propertyNotReadable", new Object[] { base.getClass().getName(), property.toString() })); + } + + Object value; + try { + value = method.invoke(base, new Object[0]); + context.setPropertyResolved(base, property); + } catch (ELException ex) { + throw ex; + } catch (InvocationTargetException ite) { + throw new ELException(ite.getCause()); + } catch (Exception ex) { + throw new ELException(ex); + } + + return value; + } + + /** + * If the base object is not <code>null</code>, attempts to set the value of the given property on this bean. + * + * <p> + * If the base is not <code>null</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object + * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after + * this method is called, the caller can safely assume no value was set. + * </p> + * + * <p> + * If this resolver was constructed in read-only mode, this method will always throw + * <code>PropertyNotWritableException</code>. + * </p> + * + * <p> + * The provided property name will first be coerced to a <code>String</code>. If property is a writable property of + * <code>base</code> (as per the JavaBeans Specification), the setter method is called (passing <code>value</code>). If + * the property exists but does not have a setter, then a <code>PropertyNotFoundException</code> is thrown. If the + * property does not exist, a <code>PropertyNotFoundException</code> is thrown. + * </p> + * + * @param context The context of this evaluation. + * @param base The bean on which to set the property. + * @param property The name of the property to set. Will be coerced to a <code>String</code>. + * @param val The value to be associated with the specified key. + * @throws NullPointerException if context is <code>null</code>. + * @throws PropertyNotFoundException if <code>base</code> is not <code>null</code> and the specified property does not + * exist. + * @throws PropertyNotWritableException if this resolver was constructed in read-only mode, or if there is no setter for + * the property. + * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown + * exception must be included as the cause property of this exception, if available. + */ + @Override + public void setValue(ELContext context, Object base, Object property, Object val) { + if (context == null) { + throw new NullPointerException(); + } + + if (base == null || property == null) { + return; + } + + if (isReadOnly) { + throw new PropertyNotWritableException(getExceptionMessageString(context, "resolverNotwritable", new Object[] { base.getClass().getName() })); + } + + Method method = getBeanProperty(context, base, property).getWriteMethod(); + if (method == null) { + throw new PropertyNotWritableException( + getExceptionMessageString(context, "propertyNotWritable", new Object[] { base.getClass().getName(), property.toString() })); + } + + try { + method.invoke(base, new Object[] { val }); + context.setPropertyResolved(base, property); + } catch (ELException ex) { + throw ex; + } catch (InvocationTargetException ite) { + throw new ELException(ite.getCause()); + } catch (Exception ex) { + if (null == val) { + val = "null"; + } + String message = getExceptionMessageString(context, "setPropertyFailed", new Object[] { property.toString(), base.getClass().getName(), val }); + throw new ELException(message, ex); + } + } + + /** + * If the base object is not <code>null</code>, invoke the method, with the given parameters on this bean. The return + * value from the method is returned. + * + * <p> + * If the base is not <code>null</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object + * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after + * this method is called, the caller should ignore the return value. + * </p> + * + * <p> + * The provided method object will first be coerced to a <code>String</code>. The methods in the bean is then examined + * and an attempt will be made to select one for invocation. If no suitable can be found, a + * <code>MethodNotFoundException</code> is thrown. + * + * If the given paramTypes is not <code>null</code>, select the method with the given name and parameter types. + * + * Else select the method with the given name that has the same number of parameters. If there are more than one such + * method, the method selection process is undefined. + * + * Else select the method with the given name that takes a variable number of arguments. + * + * Note the resolution for overloaded methods will likely be clarified in a future version of the spec. + * + * The provide parameters are coerced to the corresponding parameter types of the method, and the method is then + * invoked. + * + * @param context The context of this evaluation. + * @param base The bean on which to invoke the method + * @param methodName The simple name of the method to invoke. Will be coerced to a <code>String</code>. If method is + * "<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 Jakarta Expression Language 2.2 + */ + @Override + public Object invoke(ELContext context, Object base, Object methodName, Class<?>[] paramTypes, Object[] params) { + if (base == null || methodName == null) { + return null; + } + + Method method = ELUtil.findMethod(base.getClass(), methodName.toString(), paramTypes, params, false); + + for (Object param : params) { + // If the parameters is a LambdaExpression, set the ELContext + // for its evaluation + if (param instanceof LambdaExpression) { + ((LambdaExpression) param).setELContext(context); + } + } + + Object ret = ELUtil.invokeMethod(context, method, base, params); + context.setPropertyResolved(base, methodName); + return ret; + } + + /** + * If the base object is not <code>null</code>, returns whether a call to {@link #setValue} will always fail. + * + * <p> + * If the base is not <code>null</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object + * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after + * this method is called, the caller can safely assume no value was set. + * </p> + * + * <p> + * If this resolver was constructed in read-only mode, this method will always return <code>true</code>. + * </p> + * + * <p> + * The provided property name will first be coerced to a <code>String</code>. If property is a writable property of + * <code>base</code>, <code>false</code> is returned. If the property is found but is not writable, <code>true</code> is + * returned. If the property is not found, a <code>PropertyNotFoundException</code> is thrown. + * </p> + * + * @param context The context of this evaluation. + * @param base The bean to analyze. + * @param property The name of the property to analyzed. Will be coerced to a <code>String</code>. + * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then + * <code>true</code> if calling the <code>setValue</code> method will always fail or <code>false</code> if it is + * possible that such a call may succeed; otherwise undefined. + * @throws NullPointerException if context is <code>null</code> + * @throws PropertyNotFoundException if <code>base</code> is not <code>null</code> and the specified property does not + * exist. + * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown + * exception must be included as the cause property of this exception, if available. + */ + @Override + public boolean isReadOnly(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException(); + } + + if (base == null || property == null) { + return false; + } + + context.setPropertyResolved(true); + if (isReadOnly) { + return true; + } + + return getBeanProperty(context, base, property).isReadOnly(); + } + + /** + * If the base object is not <code>null</code>, returns an <code>Iterator</code> containing the set of JavaBeans + * properties available on the given object. Otherwise, returns <code>null</code>. + * + * <p> + * The <code>Iterator</code> returned must contain zero or more instances of {@link java.beans.FeatureDescriptor}. Each + * info object contains information about a property in the bean, as obtained by calling the + * <code>BeanInfo.getPropertyDescriptors</code> method. The <code>FeatureDescriptor</code> is initialized using the same + * fields as are present in the <code>PropertyDescriptor</code>, with the additional required named attributes + * "<code>type</code>" and "<code>resolvableAtDesignTime</code>" set as follows: + * <ul> + * <li>{@link ELResolver#TYPE} - The runtime type of the property, from + * <code>PropertyDescriptor.getPropertyType()</code>.</li> + * <li>{@link ELResolver#RESOLVABLE_AT_DESIGN_TIME} - <code>true</code>.</li> + * </ul> + * + * + * @param context The context of this evaluation. + * @param base The bean to analyze. + * @return An <code>Iterator</code> containing zero or more <code>FeatureDescriptor</code> objects, each representing a + * property on this bean, or <code>null</code> if the <code>base</code> object is <code>null</code>. + */ + @Override + public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) { + if (base == null) { + return null; + } + + BeanInfo info = null; + try { + info = Introspector.getBeanInfo(base.getClass()); + } catch (Exception ex) { + } + + if (info == null) { + return null; + } + + ArrayList<FeatureDescriptor> featureDescriptors = new ArrayList<>(info.getPropertyDescriptors().length); + for (PropertyDescriptor propertyDescriptor : info.getPropertyDescriptors()) { + propertyDescriptor.setValue("type", propertyDescriptor.getPropertyType()); + propertyDescriptor.setValue("resolvableAtDesignTime", TRUE); + featureDescriptors.add(propertyDescriptor); + } + + return featureDescriptors.iterator(); + } + + /** + * If the base object is not <code>null</code>, returns the most general type that this resolver accepts for the + * <code>property</code> argument. Otherwise, returns <code>null</code>. + * + * <p> + * Assuming the base is not <code>null</code>, this method will always return <code>Object.class</code>. This is because + * any object is accepted as a key and is coerced into a string. + * </p> + * + * @param context The context of this evaluation. + * @param base The bean to analyze. + * @return <code>null</code> if base is <code>null</code>; otherwise <code>Object.class</code>. + */ + @Override + public Class<?> getCommonPropertyType(ELContext context, Object base) { + if (base == null) { + return null; + } + + return Object.class; + } + + private BeanProperty getBeanProperty(ELContext context, Object base, Object prop) { + String property = prop.toString(); + Class<?> baseClass = base.getClass(); + + BeanProperties beanProperties = properties.get(baseClass); + if (beanProperties == null) { + beanProperties = new BeanProperties(baseClass); + properties.put(baseClass, beanProperties); + } + + BeanProperty beanProperty = beanProperties.getBeanProperty(property); + if (beanProperty == null) { + throw new PropertyNotFoundException(getExceptionMessageString(context, "propertyNotFound", new Object[] { baseClass.getName(), property })); + } + + return beanProperty; + } +}
diff --git a/api/src/main/java/javax/el/BeanNameELResolver.java b/api/src/main/java/javax/el/BeanNameELResolver.java new file mode 100644 index 0000000..9c147f1 --- /dev/null +++ b/api/src/main/java/javax/el/BeanNameELResolver.java
@@ -0,0 +1,221 @@ +/* + * Copyright (c) 2012, 2018 Oracle and/or its affiliates and others. + * All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.el; + +import java.beans.FeatureDescriptor; +import java.util.Iterator; + +/** + * <p> + * An <code>ELResolver</code> for resolving user or container managed beans. + * </p> + * <p> + * A {@link BeanNameResolver} is required for its proper operation. The following example creates an + * <code>ELResolver</code> that resolves the name "bean" to an instance of MyBean. + * + * <pre> + * <code> + * ELResovler elr = new BeanNameELResolver(new BeanNameResolver { + * public boolean isNameResolved(String beanName) { + * return "bean".equals(beanName); + * } + * public Object getBean(String beanName) { + * return "bean".equals(beanName)? new MyBean(): null; + * } + * }); + * </code> + * </pre> + * + * @since Jakarta Expression Language 3.0 + */ +public class BeanNameELResolver extends ELResolver { + + private BeanNameResolver beanNameResolver; + + /** + * Constructor + * + * @param beanNameResolver The {@link BeanNameResolver} that resolves a bean name. + */ + public BeanNameELResolver(BeanNameResolver beanNameResolver) { + this.beanNameResolver = beanNameResolver; + } + + /** + * If the base object is <code>null</code> and the property is a name that is resolvable by the BeanNameResolver, + * returns the value resolved by the BeanNameResolver. + * + * <p> + * If name is resolved by the BeanNameResolver, the <code>propertyResolved</code> property of the <code>ELContext</code> + * object must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> + * after this method is called, the caller should ignore the return value. + * </p> + * + * @param context The context of this evaluation. + * @param base <code>null</code> + * @param property The name of the bean. + * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then + * the value of the bean with the given name. Otherwise, undefined. + * @throws NullPointerException if context is <code>null</code>. + * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown + * exception must be included as the cause property of this exception, if available. + */ + @Override + public Object getValue(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException(); + } + + if (base == null && property instanceof String) { + if (beanNameResolver.isNameResolved((String) property)) { + context.setPropertyResolved(base, property); + return beanNameResolver.getBean((String) property); + } + } + + return null; + } + + /** + * If the base is null and the property is a name that is resolvable by the BeanNameResolver, the bean in the + * BeanNameResolver is set to the given value. + * + * <p> + * If the name is resolvable by the BeanNameResolver, or if the BeanNameResolver allows creating a new bean, the + * <code>propertyResolved</code> property of the <code>ELContext</code> object must be set to <code>true</code> by the + * resolver, before returning. If this property is not <code>true</code> after this method is called, the caller can + * safely assume no value has been set. + * </p> + * + * @param context The context of this evaluation. + * @param base <code>null</code> + * @param property The name of the bean + * @param value The value to set the bean with the given name to. + * @throws NullPointerException if context is <code>null</code> + * @throws PropertyNotWritableException if the BeanNameResolver does not allow the bean to be modified. + * @throws ELException if an exception was thrown while attempting to set the bean with the given name. The thrown + * exception must be included as the cause property of this exception, if available. + */ + @Override + public void setValue(ELContext context, Object base, Object property, Object value) { + if (context == null) { + throw new NullPointerException(); + } + + if (base == null && property instanceof String) { + String beanName = (String) property; + if (beanNameResolver.isNameResolved(beanName) || beanNameResolver.canCreateBean(beanName)) { + beanNameResolver.setBeanValue(beanName, value); + context.setPropertyResolved(base, property); + } + } + } + + /** + * If the base is null and the property is a name resolvable by the BeanNameResolver, return the type of the bean. + * + * <p> + * If the name is resolvable by the BeanNameResolver, the <code>propertyResolved</code> property of the + * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is + * not <code>true</code> after this method is called, the caller can safely assume no value has been set. + * </p> + * + * @param context The context of this evaluation. + * @param base <code>null</code> + * @param property The name of the bean. + * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then + * the type of the bean with the given name. Otherwise, undefined. + * @throws NullPointerException if context is <code>null</code>. + * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown + * exception must be included as the cause property of this exception, if available. + */ + @Override + public Class<?> getType(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException(); + } + + if (base == null && property instanceof String) { + if (beanNameResolver.isNameResolved((String) property)) { + context.setPropertyResolved(true); + return beanNameResolver.getBean((String) property).getClass(); + } + } + + return null; + } + + /** + * If the base is null and the property is a name resolvable by the BeanNameResolver, attempts to determine if the bean + * is writable. + * + * <p> + * If the name is resolvable by the BeanNameResolver, the <code>propertyResolved</code> property of the + * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is + * not <code>true</code> after this method is called, the caller can safely assume no value has been set. + * </p> + * + * @param context The context of this evaluation. + * @param base <code>null</code> + * @param property The name of the bean. + * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then + * <code>true</code> if the property is read-only or <code>false</code> if not; otherwise undefined. + * @throws NullPointerException if context is <code>null</code>. + * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown + * exception must be included as the cause property of this exception, if available. + */ + @Override + public boolean isReadOnly(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException(); + } + + if (base == null && property instanceof String) { + if (beanNameResolver.isNameResolved((String) property)) { + context.setPropertyResolved(true); + return beanNameResolver.isReadOnly((String) property); + } + } + + return false; + } + + /** + * Always returns <code>null</code>, since there is no reason to iterate through a list of one element: bean name. + * + * @param context The context of this evaluation. + * @param base <code>null</code>. + * @return <code>null</code>. + */ + @Override + public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) { + return null; + } + + /** + * Always returns <code>String.class</code>, since a bean name is a String. + * + * @param context The context of this evaluation. + * @param base <code>null</code>. + * @return <code>String.class</code>. + */ + @Override + public Class<?> getCommonPropertyType(ELContext context, Object base) { + return String.class; + } +}
diff --git a/api/src/main/java/javax/el/BeanNameResolver.java b/api/src/main/java/javax/el/BeanNameResolver.java new file mode 100644 index 0000000..d173174 --- /dev/null +++ b/api/src/main/java/javax/el/BeanNameResolver.java
@@ -0,0 +1,82 @@ +/* + * Copyright (c) 2012, 2018 Oracle and/or its affiliates and others. + * All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.el; + +/** + * Resolves a bean by its known name. This class can be extended to return a bean object given its name, to set a value + * to an existing bean, or to create a bean with the value. + * + * @see BeanNameELResolver + * + * @since Jakarta Expression Language 3.0 + */ +public abstract class BeanNameResolver { + + /** + * Returns whether the given name is resolved by the BeanNameResolver + * + * @param beanName The name of the bean. + * @return true if the name is resolved by this BeanNameResolver; false otherwise. + */ + public boolean isNameResolved(String beanName) { + return false; + } + + /** + * Returns the bean known by its name. + * + * @param beanName The name of the bean. + * @return The bean with the given name. Can be <code>null</code>. + * + */ + public Object getBean(String beanName) { + return null; + } + + /** + * Sets a value to a bean of the given name. If the bean of the given name does not exist and if {@link #canCreateBean} + * is <code>true</code>, one is created with the given value. + * + * @param beanName The name of the bean + * @param value The value to set the bean to. Can be <code>null</code>. + * @throws PropertyNotWritableException if the bean cannot be modified or created. + */ + public void setBeanValue(String beanName, Object value) throws PropertyNotWritableException { + throw new PropertyNotWritableException(); + } + + /** + * Indicates if the bean of the given name is read-only or writable + * + * @param beanName The name of the bean + * @return <code>true</code> if the bean can be set to a new value. <code>false</code> otherwise. + */ + public boolean isReadOnly(String beanName) { + return true; + } + + /** + * Allow creating a bean of the given name if it does not exist. + * + * @param beanName The name of the bean + * @return <code>true</code> if bean creation is supported <code>false</code> otherwise. + */ + public boolean canCreateBean(String beanName) { + return false; + } +}
diff --git a/api/src/main/java/javax/el/CompositeELResolver.java b/api/src/main/java/javax/el/CompositeELResolver.java new file mode 100644 index 0000000..1e0e9e1 --- /dev/null +++ b/api/src/main/java/javax/el/CompositeELResolver.java
@@ -0,0 +1,539 @@ +/* + * Copyright (c) 1997-2018 Oracle and/or its affiliates and others. + * All rights reserved. + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.el; + +import java.beans.FeatureDescriptor; +import java.util.Iterator; + +/** + * Maintains an ordered composite list of child <code>ELResolver</code>s. + * + * <p> + * Though only a single <code>ELResolver</code> is associated with an <code>ELContext</code>, there are usually multiple + * resolvers considered for any given variable or property resolution. <code>ELResolver</code>s are combined together + * using a <code>CompositeELResolver</code>, to define rich semantics for evaluating an expression. + * </p> + * + * <p> + * For the {@link #getValue}, {@link #getType}, {@link #setValue} and {@link #isReadOnly} methods, an + * <code>ELResolver</code> is not responsible for resolving all possible (base, property) pairs. In fact, most resolvers + * will only handle a <code>base</code> of a single type. To indicate that a resolver has successfully resolved a + * particular (base, property) pair, it must set the <code>propertyResolved</code> property of the + * <code>ELContext</code> to <code>true</code>. If it could not handle the given pair, it must leave this property + * alone. The caller must ignore the return value of the method if <code>propertyResolved</code> is <code>false</code>. + * </p> + * + * <p> + * The <code>CompositeELResolver</code> initializes the <code>ELContext.propertyResolved</code> flag to + * <code>false</code>, and uses it as a stop condition for iterating through its component resolvers. + * </p> + * + * <p> + * The <code>ELContext.propertyResolved</code> flag is not used for the design-time methods + * {@link #getFeatureDescriptors} and {@link #getCommonPropertyType}. Instead, results are collected and combined from + * all child <code>ELResolver</code>s for these methods. + * </p> + * + * @see ELContext + * @see ELResolver + * @since Jakarta Server Pages 2.1 + */ +public class CompositeELResolver extends ELResolver { + + public CompositeELResolver() { + this.size = 0; + this.elResolvers = new ELResolver[16]; + } + + /** + * Adds the given resolver to the list of component resolvers. + * + * <p> + * Resolvers are consulted in the order in which they are added. + * </p> + * + * @param elResolver The component resolver to add. + * @throws NullPointerException If the provided resolver is <code>null</code>. + */ + public void add(ELResolver elResolver) { + if (elResolver == null) { + throw new NullPointerException(); + } + + if (size >= elResolvers.length) { + ELResolver[] newResolvers = new ELResolver[size * 2]; + System.arraycopy(elResolvers, 0, newResolvers, 0, size); + elResolvers = newResolvers; + } + + elResolvers[size++] = elResolver; + } + + /** + * Attempts to resolve the given <code>property</code> object on the given <code>base</code> object by querying all + * component resolvers. + * + * <p> + * If this resolver handles the given (base, property) pair, the <code>propertyResolved</code> property of the + * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is + * not <code>true</code> after this method is called, the caller should ignore the return value. + * + * <p> + * First, <code>propertyResolved</code> is set to <code>false</code> on the provided <code>ELContext</code>. + * + * <p> + * Next, for each component resolver in this composite: + * <ol> + * <li>The <code>getValue()</code> method is called, passing in the provided <code>context</code>, <code>base</code> and + * <code>property</code>.</li> + * <li>If the <code>ELContext</code>'s <code>propertyResolved</code> flag is <code>false</code> then iteration + * continues.</li> + * <li>Otherwise, iteration stops and no more component resolvers are considered. The value returned by + * <code>getValue()</code> is returned by this method.</li> + * </ol> + * + * <p> + * If none of the component resolvers were able to perform this operation, the value <code>null</code> is returned and + * the <code>propertyResolved</code> flag remains set to <code>false</code>. + * + * <p> + * Any exception thrown by component resolvers during the iteration is propagated to the caller of this method. + * + * @param context The context of this evaluation. + * @param base The base object whose property value is to be returned, or <code>null</code> to resolve a top-level + * variable. + * @param property The property or variable to be resolved. + * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then + * the result of the variable or property resolution; otherwise undefined. + * @throws NullPointerException if context is <code>null</code> + * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but + * the specified variable or property does not exist or is not readable. + * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown + * exception must be included as the cause property of this exception, if available. + */ + @Override + public Object getValue(ELContext context, Object base, Object property) { + context.setPropertyResolved(false); + + Object value = null; + for (int i = 0; i < size; i++) { + value = elResolvers[i].getValue(context, base, property); + if (context.isPropertyResolved()) { + return value; + } + } + + return null; + } + + /** + * Attempts to resolve and invoke the given <code>method</code> on the given <code>base</code> object by querying all + * component resolvers. + * + * <p> + * If this resolver handles the given (base, method) pair, the <code>propertyResolved</code> property of the + * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is + * not <code>true</code> after this method is called, the caller should ignore the return value. + * </p> + * + * <p> + * First, <code>propertyResolved</code> is set to <code>false</code> on the provided <code>ELContext</code>. + * </p> + * + * <p> + * Next, for each component resolver in this composite: + * <ol> + * <li>The <code>invoke()</code> method is called, passing in the provided <code>context</code>, <code>base</code>, + * <code>method</code>, <code>paramTypes</code>, and <code>params</code>.</li> + * <li>If the <code>ELContext</code>'s <code>propertyResolved</code> flag is <code>false</code> then iteration + * continues.</li> + * <li>Otherwise, iteration stops and no more component resolvers are considered. The value returned by + * <code>getValue()</code> is returned by this method.</li> + * </ol> + * + * <p> + * If none of the component resolvers were able to perform this operation, the value <code>null</code> is returned and + * the <code>propertyResolved</code> flag remains set to <code>false</code> + * </p> + * . + * + * <p> + * Any exception thrown by component resolvers during the iteration is propagated to the caller of this method. + * </p> + * + * @param context The context of this evaluation. + * @param base The bean on which to invoke the method + * @param method The simple name of the method to invoke. Will be coerced to a <code>String</code>. + * @param paramTypes An array of Class objects identifying the method's formal parameter types, in declared order. Use + * an empty array if the method has no parameters. Can be <code>null</code>, in which case the method's formal parameter + * types are assumed to be unknown. + * @param params The parameters to pass to the method, or <code>null</code> if no parameters. + * + * @return The result of the method invocation (<code>null</code> if the method has a <code>void</code> return type). + * + * @since Jakarta Expression Language 2.2 + */ + @Override + public Object invoke(ELContext context, Object base, Object method, Class<?>[] paramTypes, Object[] params) { + context.setPropertyResolved(false); + + Object value; + for (int i = 0; i < size; i++) { + value = elResolvers[i].invoke(context, base, method, paramTypes, params); + if (context.isPropertyResolved()) { + return value; + } + } + + return null; + } + + /** + * For a given <code>base</code> and <code>property</code>, attempts to identify the most general type that is + * acceptable for an object to be passed as the <code>value</code> parameter in a future call to the {@link #setValue} + * method. The result is obtained by querying all component resolvers. + * + * <p> + * If this resolver handles the given (base, property) pair, the <code>propertyResolved</code> property of the + * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is + * not <code>true</code> after this method is called, the caller should ignore the return value. + * </p> + * + * <p> + * First, <code>propertyResolved</code> is set to <code>false</code> on the provided <code>ELContext</code>. + * </p> + * + * <p> + * Next, for each component resolver in this composite: + * <ol> + * <li>The <code>getType()</code> method is called, passing in the provided <code>context</code>, <code>base</code> and + * <code>property</code>.</li> + * <li>If the <code>ELContext</code>'s <code>propertyResolved</code> flag is <code>false</code> then iteration + * continues.</li> + * <li>Otherwise, iteration stops and no more component resolvers are considered. The value returned by + * <code>getType()</code> is returned by this method.</li> + * </ol> + * + * <p> + * If none of the component resolvers were able to perform this operation, the value <code>null</code> is returned and + * the <code>propertyResolved</code> flag remains set to <code>false</code>. + * </p> + * + * + * <p> + * Any exception thrown by component resolvers during the iteration is propagated to the caller of this method. + * </p> + * + * @param context The context of this evaluation. + * @param base The base object whose property value is to be analyzed, or <code>null</code> to analyze a top-level + * variable. + * @param property The property or variable to return the acceptable type for. + * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then + * the most general acceptable type; otherwise undefined. + * @throws NullPointerException if context is <code>null</code> + * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but + * the specified variable or property does not exist or is not readable. + * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown + * exception must be included as the cause property of this exception, if available. + */ + @Override + public Class<?> getType(ELContext context, Object base, Object property) { + context.setPropertyResolved(false); + + Class<?> type; + for (int i = 0; i < size; i++) { + type = elResolvers[i].getType(context, base, property); + if (context.isPropertyResolved()) { + return type; + } + } + + return null; + } + + /** + * Attempts to set the value of the given <code>property</code> object on the given <code>base</code> object. All + * component resolvers are asked to attempt to set the value. + * + * <p> + * If this resolver handles the given (base, property) pair, the <code>propertyResolved</code> property of the + * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is + * not <code>true</code> after this method is called, the caller can safely assume no value has been set. + * + * <p> + * First, <code>propertyResolved</code> is set to <code>false</code> on the provided <code>ELContext</code>. + * + * <p> + * Next, for each component resolver in this composite: + * <ol> + * <li>The <code>setValue()</code> method is called, passing in the provided <code>context</code>, <code>base</code>, + * <code>property</code> and <code>value</code>.</li> + * <li>If the <code>ELContext</code>'s <code>propertyResolved</code> flag is <code>false</code> then iteration + * continues.</li> + * <li>Otherwise, iteration stops and no more component resolvers are considered.</li> + * </ol> + * + * <p> + * If none of the component resolvers were able to perform this operation, the <code>propertyResolved</code> flag + * remains set to <code>false</code>. + * + * <p> + * Any exception thrown by component resolvers during the iteration is propagated to the caller of this method. + * + * @param context The context of this evaluation. + * @param base The base object whose property value is to be set, or <code>null</code> to set a top-level variable. + * @param property The property or variable to be set. + * @param val The value to set the property or variable to. + * @throws NullPointerException if context is <code>null</code> + * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but + * the specified variable or property does not exist. + * @throws PropertyNotWritableException if the given (base, property) pair is handled by this <code>ELResolver</code> + * but the specified variable or property is not writable. + * @throws ELException if an exception was thrown while attempting to set the property or variable. The thrown exception + * must be included as the cause property of this exception, if available. + */ + @Override + public void setValue(ELContext context, Object base, Object property, Object val) { + context.setPropertyResolved(false); + + for (int i = 0; i < size; i++) { + elResolvers[i].setValue(context, base, property, val); + if (context.isPropertyResolved()) { + return; + } + } + } + + /** + * For a given <code>base</code> and <code>property</code>, attempts to determine whether a call to {@link #setValue} + * will always fail. The result is obtained by querying all component resolvers. + * + * <p> + * If this resolver handles the given (base, property) pair, the <code>propertyResolved</code> property of the + * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is + * not <code>true</code> after this method is called, the caller should ignore the return value. + * </p> + * + * <p> + * First, <code>propertyResolved</code> is set to <code>false</code> on the provided <code>ELContext</code>. + * </p> + * + * <p> + * Next, for each component resolver in this composite: + * <ol> + * <li>The <code>isReadOnly()</code> method is called, passing in the provided <code>context</code>, <code>base</code> + * and <code>property</code>.</li> + * <li>If the <code>ELContext</code>'s <code>propertyResolved</code> flag is <code>false</code> then iteration + * continues.</li> + * <li>Otherwise, iteration stops and no more component resolvers are considered. The value returned by + * <code>isReadOnly()</code> is returned by this method.</li> + * </ol> + * + * <p> + * If none of the component resolvers were able to perform this operation, the value <code>false</code> is returned and + * the <code>propertyResolved</code> flag remains set to <code>false</code> + * </p> + * . + * + * <p> + * Any exception thrown by component resolvers during the iteration is propagated to the caller of this method. + * </p> + * + * @param context The context of this evaluation. + * @param base The base object whose property value is to be analyzed, or <code>null</code> to analyze a top-level + * variable. + * @param property The property or variable to return the read-only status for. + * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then + * <code>true</code> if the property is read-only or <code>false</code> if not; otherwise undefined. + * @throws NullPointerException if context is <code>null</code> + * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but + * the specified variable or property does not exist. + * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown + * exception must be included as the cause property of this exception, if available. + */ + @Override + public boolean isReadOnly(ELContext context, Object base, Object property) { + context.setPropertyResolved(false); + + boolean readOnly; + for (int i = 0; i < size; i++) { + readOnly = elResolvers[i].isReadOnly(context, base, property); + if (context.isPropertyResolved()) { + return readOnly; + } + } + + return false; // Does not matter + } + + /** + * Returns information about the set of variables or properties that can be resolved for the given <code>base</code> + * object. One use for this method is to assist tools in auto-completion. The results are collected from all component + * resolvers. + * + * <p> + * The <code>propertyResolved</code> property of the <code>ELContext</code> is not relevant to this method. The results + * of all <code>ELResolver</code>s are concatenated. + * </p> + * + * <p> + * The <code>Iterator</code> returned is an iterator over the collection of <code>FeatureDescriptor</code> objects + * returned by the iterators returned by each component resolver's <code>getFeatureDescriptors</code> method. If + * <code>null</code> is returned by a resolver, it is skipped. + * </p> + * + * @param context The context of this evaluation. + * @param base The base object whose set of valid properties is to be enumerated, or <code>null</code> to enumerate the + * set of top-level variables that this resolver can evaluate. + * @return An <code>Iterator</code> containing zero or more (possibly infinitely more) <code>FeatureDescriptor</code> + * objects, or <code>null</code> if this resolver does not handle the given <code>base</code> object or that the results + * are too complex to represent with this method + */ + @Override + public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) { + return new CompositeIterator(elResolvers, size, context, base); + } + + /** + * Returns the most general type that this resolver accepts for the <code>property</code> argument, given a + * <code>base</code> object. One use for this method is to assist tools in auto-completion. The result is obtained by + * querying all component resolvers. + * + * <p> + * The <code>Class</code> returned is the most specific class that is a common superclass of all the classes returned by + * each component resolver's <code>getCommonPropertyType</code> method. If <code>null</code> is returned by a resolver, + * it is skipped. + * + * @param context The context of this evaluation. + * @param base The base object to return the most general property type for, or <code>null</code> to enumerate the set + * of top-level variables that this resolver can evaluate. + * + * @return <code>null</code> if this <code>ELResolver</code> does not know how to handle the given <code>base</code> + * object; otherwise <code>Object.class</code> if any type of <code>property</code> is accepted; otherwise the most + * general <code>property</code> type accepted for the given <code>base</code>. + */ + @Override + public Class<?> getCommonPropertyType(ELContext context, Object base) { + Class<?> commonPropertyType = null; + for (int i = 0; i < size; i++) { + + Class<?> type = elResolvers[i].getCommonPropertyType(context, base); + if (type == null) { + // skip this Jakarta Expression Language Resolver + continue; + } else if (commonPropertyType == null) { + commonPropertyType = type; + } else if (commonPropertyType.isAssignableFrom(type)) { + continue; + } else if (type.isAssignableFrom(commonPropertyType)) { + commonPropertyType = type; + } else { + // Don't have a commonPropertyType + return null; + } + } + + return commonPropertyType; + } + + /** + * Converts an object to a specific type. + * + * <p> + * An <code>ELException</code> is thrown if an error occurs during the conversion. + * </p> + * + * @param context The context of this evaluation. + * @param obj The object to convert. + * @param targetType The target type for the convertion. + * + * @throws ELException thrown if errors occur. + * + * @since Jakarta Expression Language 3.0 + */ + @Override + public Object convertToType(ELContext context, Object obj, Class<?> targetType) { + context.setPropertyResolved(false); + + Object value = null; + for (int i = 0; i < size; i++) { + value = elResolvers[i].convertToType(context, obj, targetType); + if (context.isPropertyResolved()) { + return value; + } + } + + return null; + } + + private ELResolver[] elResolvers; + private int size; + + private static class CompositeIterator implements Iterator<FeatureDescriptor> { + + ELResolver[] resolvers; + int size; + int index = 0; + Iterator<FeatureDescriptor> propertyIter = null; + ELContext context; + Object base; + + CompositeIterator(ELResolver[] resolvers, int size, ELContext context, Object base) { + this.resolvers = resolvers; + this.size = size; + this.context = context; + this.base = base; + } + + @Override + public boolean hasNext() { + if (propertyIter == null || !propertyIter.hasNext()) { + while (index < size) { + ELResolver elResolver = resolvers[index++]; + propertyIter = elResolver.getFeatureDescriptors(context, base); + if (propertyIter != null) { + return propertyIter.hasNext(); + } + } + return false; + } + return propertyIter.hasNext(); + } + + @Override + public FeatureDescriptor next() { + if (propertyIter == null || !propertyIter.hasNext()) { + while (index < size) { + ELResolver elResolver = resolvers[index++]; + propertyIter = elResolver.getFeatureDescriptors(context, base); + if (propertyIter != null) { + return propertyIter.next(); + } + } + return null; + } + return propertyIter.next(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } +}
diff --git a/api/src/main/java/javax/el/ELClass.java b/api/src/main/java/javax/el/ELClass.java new file mode 100644 index 0000000..86355ed --- /dev/null +++ b/api/src/main/java/javax/el/ELClass.java
@@ -0,0 +1,51 @@ +/* + * Copyright (c) 2013, 2018 Oracle and/or its affiliates and others. + * All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.el; + +/** + * A runtime representation of a Class in the Jakarta Expression Language expressions. It encapsulates the + * java.lang.Class instance. + * + * <p> + * This class is used only in {@link StaticFieldELResolver} and will probably only be of interest to Jakarta Expression + * Language implementors, and not Jakarta Expression Language users. + * + * @since Jakarta Expression Language 3.0 + */ +public class ELClass { + + private Class<?> klass; + + /** + * Constructor + * + * @param klass The Class instance + */ + public ELClass(Class<?> klass) { + this.klass = klass; + } + + /** + * Returns the Class instance + * + * @return The Class instance + */ + public Class<?> getKlass() { + return this.klass; + } +}
diff --git a/api/src/main/java/javax/el/ELContext.java b/api/src/main/java/javax/el/ELContext.java new file mode 100644 index 0000000..0b95e59 --- /dev/null +++ b/api/src/main/java/javax/el/ELContext.java
@@ -0,0 +1,448 @@ +/* + * Copyright (c) 1997-2018 Oracle and/or its affiliates and others. + * All rights reserved. + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.el; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Stack; + +/** + * Context information for expression parsing and evaluation. + * + * <p> + * To parse or evaluate an {@link Expression}, an <code>ELContext</code> must be provided. The <code>ELContext</code> + * holds: + * <ul> + * <li>a reference to {@link FunctionMapper} that will be used to resolve Jakarta Expression Language Functions. This is + * used only in parsing.</li> + * <li>a reference to {@link VariableMapper} that will be used to resolve Jakarta Expression Language Variables. This is + * used only in parsing.</li> + * <li>a reference to the base {@link ELResolver} that will be consulted to resolve model objects and their + * properties</li> + * <li>a collection of all the relevant context objects for use by <code>ELResolver</code>s</li> + * <li>state information during the evaluation of an expression, such as whether a property has been resolved yet</li> + * <li>a reference to {@link ImportHandler} that will be consulted to resolve classes that have been imported</li> + * <li>a reference to the arguments for the active {@link LambdaExpression}s</li> + * <li>a reference to the list of registered evaluation listeners</li> + * </ul> + * + * <p> + * The collection of context objects is necessary because each <code>ELResolver</code> may need access to a different + * context object. For example, Jakarta Server Pages and Jakarta Faces resolvers need access to a + * <code>javax.servlet.jsp.JspContext</code> and a <code>javax.faces.context.FacesContext</code>, respectively. + * + * <p> + * When used in a web container, the creation of <code>ELContext</code> objects is controlled through the underlying + * technology. For example, in Jakarta Server Pages the <code>JspContext.getELContext()</code> factory method is used. + * Some technologies provide the ability to add an {@link ELContextListener} so that applications and frameworks can + * ensure their own context objects are attached to any newly created <code>ELContext</code>. + * + * <p> + * When used in a stand-alone environment, {@link StandardELContext} provides a default <code>ELContext</code>, which is + * managed and modified by {@link ELManager}. + * + * <p> + * Because it stores state during expression evaluation, an <code>ELContext</code> object is not thread-safe. Care + * should be taken to never share an <code>ELContext</code> instance between two or more threads. + * + * @see ELContextListener + * @see ELContextEvent + * @see ELResolver + * @see FunctionMapper + * @see VariableMapper + * @see ImportHandler + * @see LambdaExpression + * @see StandardELContext + * + * @since Jakarta Expression Language 2.1 and Jakarta Expression Language 3.0 + */ +public abstract class ELContext { + + private boolean resolved; + private HashMap<Class<?>, Object> map = new HashMap<>(); + private transient List<EvaluationListener> listeners; + private Stack<Map<String, Object>> lambdaArgs; + private ImportHandler importHandler; + private Locale locale; + + /** + * Called to indicate that a <code>ELResolver</code> has successfully resolved a given (base, property) pair. Use + * {@link #setPropertyResolved(Object, Object)} if resolved is true and to notify {@link EvaluationListener}s. + * + * <p> + * The {@link CompositeELResolver} checks this property to determine whether it should consider or skip other component + * resolvers. + * </p> + * + * @see CompositeELResolver + * @param resolved true if the property has been resolved, or false if not. + */ + public void setPropertyResolved(boolean resolved) { + this.resolved = resolved; + } + + /** + * Called to indicate that a <code>ELResolver</code> has successfully resolved a given (base, property) pair and to + * notify the {@link EvaluationListener}s. + * + * <p> + * The {@link CompositeELResolver} checks this property to determine whether it should consider or skip other component + * resolvers. + * + * @see CompositeELResolver + * @param base The base object + * @param property The property object + * + * @since Jakarta Expression Language 3.0 + */ + public void setPropertyResolved(Object base, Object property) { + setPropertyResolved(true); // Don't set the variable here, for 2.2 users + // ELContext may be overridden or delegated. + notifyPropertyResolved(base, property); + } + + /** + * Returns whether an {@link ELResolver} has successfully resolved a given (base, property) pair. + * + * <p> + * The {@link CompositeELResolver} checks this property to determine whether it should consider or skip other component + * resolvers. + * + * @see CompositeELResolver + * @return true if the property has been resolved, or false if not. + */ + public boolean isPropertyResolved() { + return resolved; + } + + /** + * Associates a context object with this <code>ELContext</code>. + * + * <p> + * The <code>ELContext</code> maintains a collection of context objects relevant to the evaluation of an expression. + * These context objects are used by <code>ELResolver</code>s. This method is used to add a context object to that + * collection. + * </p> + * + * <p> + * By convention, the <code>contextObject</code> will be of the type specified by the <code>key</code>. However, this is + * not required and the key is used strictly as a unique identifier. + * </p> + * + * @param key The key used by an @{link ELResolver} to identify this context object. + * @param contextObject The context object to add to the collection. + * @throws NullPointerException if key is null or contextObject is null. + */ + public void putContext(Class key, Object contextObject) { + if (key == null || contextObject == null) { + throw new NullPointerException(); + } + + map.put(key, contextObject); + } + + /** + * Returns the context object associated with the given key. + * + * <p> + * The <code>ELContext</code> maintains a collection of context objects relevant to the evaluation of an expression. + * These context objects are used by <code>ELResolver</code>s. This method is used to retrieve the context with the + * given key from the collection. + * </p> + * + * <p> + * By convention, the object returned will be of the type specified by the <code>key</code>. However, this is not + * required and the key is used strictly as a unique identifier. + * </p> + * + * @param key The unique identifier that was used to associate the context object with this <code>ELContext</code>. + * @return The context object associated with the given key, or null if no such context was found. + * @throws NullPointerException if key is null. + */ + public Object getContext(Class key) { + if (key == null) { + throw new NullPointerException(); + } + + return map.get(key); + } + + /** + * Retrieves the <code>ELResolver</code> associated with this context. + * + * <p> + * The <code>ELContext</code> maintains a reference to the <code>ELResolver</code> that will be consulted to resolve + * variables and properties during an expression evaluation. This method retrieves the reference to the resolver. + * </p> + * + * <p> + * Once an <code>ELContext</code> is constructed, the reference to the <code>ELResolver</code> associated with the + * context cannot be changed. + * </p> + * + * @return The resolver to be consulted for variable and property resolution during expression evaluation. + */ + public abstract ELResolver getELResolver(); + + /** + * Retrieves the <code>ImportHandler</code> associated with this <code>ELContext</code>. + * + * @return The import handler to manage imports of classes and packages. + * + * @since Jakarta Expression Language 3.0 + */ + public ImportHandler getImportHandler() { + if (importHandler == null) { + importHandler = new ImportHandler(); + } + + return importHandler; + } + + /** + * Retrieves the <code>FunctionMapper</code> associated with this <code>ELContext</code>. + * + * @return The function mapper to be consulted for the resolution of Jakarta Expression Language functions. + */ + public abstract FunctionMapper getFunctionMapper(); + + /** + * Get the <code>Locale</code> stored by a previous invocation to {@link #setLocale}. If this method returns non + * <code>null</code>, this <code>Locale</code> must be used for all localization needs in the implementation. The + * <code>Locale</code> must not be cached to allow for applications that change <code>Locale</code> dynamically. + * + * @return The <code>Locale</code> in which this instance is operating. Used primarily for message localization. + */ + public Locale getLocale() { + return locale; + } + + /** + * Sets the <code>Locale</code> for this instance. + * + * <p> + * This method may be called by the party creating the instance, such as Jakarta Faces or Jakarta Server Pages, to + * enable the Jakarta Expression Language implementation to provide localized messages to the user. If no + * <code>Locale</code> is set, the implementation must use the locale returned by <code>Locale.getDefault( )</code>. + * + * @param locale the locale for this instance + */ + public void setLocale(Locale locale) { + this.locale = locale; + } + + /** + * Retrieves the <code>VariableMapper</code> associated with this <code>ELContext</code>. + * + * @return The variable mapper to be consulted for the resolution of Jakarta Expression Language variables. + */ + public abstract VariableMapper getVariableMapper(); + + /** + * Registers an evaluation listener to the ELContext. + * + * @param listener The listener to be added. + * + * @since Jakarta Expression Language 3.0 + */ + public void addEvaluationListener(EvaluationListener listener) { + if (listeners == null) { + listeners = new ArrayList<>(); + } + + listeners.add(listener); + } + + /** + * Returns the list of registered evaluation listeners. + * + * @return The list of registered evaluation listeners. + * + * @since Jakarta Expression Language 3.0 + */ + public List<EvaluationListener> getEvaluationListeners() { + return listeners; + } + + /** + * Notifies the listeners before an Jakarta Expression Language expression is evaluated + * + * @param expr The Jakarta Expression Language expression string to be evaluated + */ + public void notifyBeforeEvaluation(String expr) { + if (getEvaluationListeners() == null) { + return; + } + + for (EvaluationListener listener : getEvaluationListeners()) { + listener.beforeEvaluation(this, expr); + } + } + + /** + * Notifies the listeners after an Jakarta Expression Language expression is evaluated + * + * @param expr The Jakarta Expression Language expression string that has been evaluated + */ + public void notifyAfterEvaluation(String expr) { + if (getEvaluationListeners() == null) { + return; + } + + for (EvaluationListener listener : getEvaluationListeners()) { + listener.afterEvaluation(this, expr); + } + } + + /** + * Notifies the listeners when the (base, property) pair is resolved + * + * @param base The base object + * @param property The property Object + */ + public void notifyPropertyResolved(Object base, Object property) { + if (getEvaluationListeners() == null) { + return; + } + + for (EvaluationListener listener : getEvaluationListeners()) { + listener.propertyResolved(this, base, property); + } + } + + /** + * Inquires if the name is a LambdaArgument + * + * @param arg A possible Lambda formal parameter name + * @return true if arg is a LambdaArgument, false otherwise. + */ + public boolean isLambdaArgument(String arg) { + if (lambdaArgs == null) { + return false; + } + + for (int i = lambdaArgs.size() - 1; i >= 0; i--) { + Map<String, Object> lmap = lambdaArgs.elementAt(i); + if (lmap.containsKey(arg)) { + return true; + } + } + + return false; + } + + /** + * Retrieves the Lambda argument associated with a formal parameter. If the Lambda expression is nested within other + * Lambda expressions, the arguments for the current Lambda expression is first searched, and if not found, the + * arguments for the immediate nesting Lambda expression then searched, and so on. + * + * @param arg The formal parameter for the Lambda argument + * @return The object associated with formal parameter. Null if no object has been associated with the parameter. + * + * @since Jakarta Expression Language 3.0 + */ + public Object getLambdaArgument(String arg) { + if (lambdaArgs == null) { + return null; + } + + for (int i = lambdaArgs.size() - 1; i >= 0; i--) { + Map<String, Object> lmap = lambdaArgs.elementAt(i); + Object v = lmap.get(arg); + if (v != null) { + return v; + } + } + + return null; + } + + /** + * Installs a Lambda argument map, in preparation for the evaluation of a Lambda expression. The arguments in the map + * will be in scope during the evaluation of the Lambda expression. + * + * @param args The Lambda arguments map + * + * @since Jakarta Expression Language 3.0 + */ + public void enterLambdaScope(Map<String, Object> args) { + if (lambdaArgs == null) { + lambdaArgs = new Stack<>(); + } + + lambdaArgs.push(args); + } + + /** + * Exits the Lambda expression evaluation. The Lambda argument map that was previously installed is removed. + * + * @since Jakarta Expression Language 3.0 + */ + public void exitLambdaScope() { + if (lambdaArgs != null) { + lambdaArgs.pop(); + } + } + + /** + * Converts an object to a specific type. If a custom converter in the <code>ELResolver</code> handles this conversion, + * it is used. Otherwise the standard coercions is applied. + * + * <p> + * An <code>ELException</code> is thrown if an error occurs during the conversion. + * + * @param obj The object to convert. + * @param targetType The target type for the conversion. + * + * @return object converted to <code>targetType</code> + * @throws ELException thrown if errors occur. + * + * @since Jakarta Expression Language 3.0 + */ + public Object convertToType(Object obj, Class<?> targetType) { + boolean propertyResolvedSave = isPropertyResolved(); + try { + setPropertyResolved(false); + ELResolver elResolver = getELResolver(); + if (elResolver != null) { + Object res = elResolver.convertToType(this, obj, targetType); + if (isPropertyResolved()) { + return res; + } + } + } catch (ELException ex) { + throw ex; + } catch (Exception ex) { + throw new ELException(ex); + } finally { + setPropertyResolved(propertyResolvedSave); + } + + ExpressionFactory exprFactory = (ExpressionFactory) getContext(ExpressionFactory.class); + if (exprFactory == null) { + exprFactory = ELUtil.getExpressionFactory(); + } + + return exprFactory.coerceToType(obj, targetType); + } + +}
diff --git a/api/src/main/java/javax/el/ELContextEvent.java b/api/src/main/java/javax/el/ELContextEvent.java new file mode 100644 index 0000000..e59cc16 --- /dev/null +++ b/api/src/main/java/javax/el/ELContextEvent.java
@@ -0,0 +1,53 @@ +/* + * Copyright (c) 1997-2018 Oracle and/or its affiliates and others. + * All rights reserved. + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.el; + +import java.util.EventObject; + +/** + * An event which indicates that an {@link ELContext} has been created. The source object is the ELContext that was + * created. + * + * @see ELContext + * @see ELContextListener + * + * @since Jakarta Server Pages 2.1 + */ +public class ELContextEvent extends EventObject { + + private static final long serialVersionUID = 1255131906285426769L; + + /** + * Constructs an ELContextEvent object to indicate that an <code>ELContext</code> has been created. + * + * @param source the <code>ELContext</code> that was created. + */ + public ELContextEvent(ELContext source) { + super(source); + } + + /** + * Returns the <code>ELContext</code> that was created. This is a type-safe equivalent of the {@link #getSource} method. + * + * @return the ELContext that was created. + */ + public ELContext getELContext() { + return (ELContext) getSource(); + } +}
diff --git a/api/src/main/java/javax/el/ELContextListener.java b/api/src/main/java/javax/el/ELContextListener.java new file mode 100644 index 0000000..b6c663b --- /dev/null +++ b/api/src/main/java/javax/el/ELContextListener.java
@@ -0,0 +1,38 @@ +/* + * Copyright (c) 1997-2018 Oracle and/or its affiliates and others. + * All rights reserved. + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.el; + +/** + * The listener interface for receiving notification when an {@link ELContext} is created. + * + * @see ELContext + * @see ELContextEvent + * + * @since Jakarta Server Pages 2.1 + */ +public interface ELContextListener extends java.util.EventListener { + + /** + * Invoked when a new <code>ELContext</code> has been created. + * + * @param ece the notification event. + */ + void contextCreated(ELContextEvent ece); + +}
diff --git a/api/src/main/java/javax/el/ELException.java b/api/src/main/java/javax/el/ELException.java new file mode 100644 index 0000000..eb9bcbc --- /dev/null +++ b/api/src/main/java/javax/el/ELException.java
@@ -0,0 +1,69 @@ +/* + * Copyright (c) 1997-2018 Oracle and/or its affiliates and others. + * All rights reserved. + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.el; + +/** + * Represents any of the exception conditions that can arise during expression evaluation. + * + * @since Jakarta Server Pages 2.1 + */ +public class ELException extends RuntimeException { + + private static final long serialVersionUID = -2161386187282690885L; + + // ------------------------------------- + /** + * Creates an <code>ELException</code> with no detail message. + */ + public ELException() { + super(); + } + + // ------------------------------------- + /** + * Creates an <code>ELException</code> with the provided detail message. + * + * @param pMessage the detail message + */ + public ELException(String pMessage) { + super(pMessage); + } + + // ------------------------------------- + /** + * Creates an <code>ELException</code> with the given cause. + * + * @param pRootCause the originating cause of this exception + */ + public ELException(Throwable pRootCause) { + super(pRootCause); + } + + // ------------------------------------- + /** + * Creates an ELException with the given detail message and root cause. + * + * @param pMessage the detail message + * @param pRootCause the originating cause of this exception + */ + public ELException(String pMessage, Throwable pRootCause) { + super(pMessage, pRootCause); + } + +}
diff --git a/api/src/main/java/javax/el/ELManager.java b/api/src/main/java/javax/el/ELManager.java new file mode 100644 index 0000000..5c56070 --- /dev/null +++ b/api/src/main/java/javax/el/ELManager.java
@@ -0,0 +1,163 @@ +/* + * Copyright (c) 2013, 2018 Oracle and/or its affiliates and others. + * All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.el; + +import java.lang.reflect.Method; + +/** + * Manages Jakarta Expression Language parsing and evaluation environment. The ELManager maintains an instance of + * ExpressionFactory and StandardELContext, for parsing and evaluating Jakarta Expression Language expressions. + * + * @since Jakarta Expression Language 3.0 + */ +public class ELManager { + + private StandardELContext elContext; + + /** + * Return the ExpressionFactory instance used for Jakarta Expression Language evaluations. + * + * @return The ExpressionFactory + */ + public static ExpressionFactory getExpressionFactory() { + return ELUtil.getExpressionFactory(); + } + + /** + * Return the ELContext used for parsing and evaluating Jakarta Expression Language expressions. If there is currently + * no ELContext, a default instance of StandardELContext is returned. + * + * @return The ELContext used for parsing and evaluating Jakarta Expression Language expressions.. + */ + public StandardELContext getELContext() { + if (elContext == null) { + elContext = new StandardELContext(getExpressionFactory()); + } + + return elContext; + } + + /** + * Set the ELContext used for parsing and evaluating Jakarta Expression Language expressions. The supplied ELContext + * will not be modified, except for the context object map. + * + * @param context The new ELContext. + * @return The previous ELContext, null if none. + */ + public ELContext setELContext(ELContext context) { + ELContext prevELContext = elContext; + elContext = new StandardELContext(context); + return prevELContext; + } + + /** + * Register a BeanNameResolver. Construct a BeanNameELResolver with the BeanNameResolver and add it to the list of + * ELResolvers. Once registered, the BeanNameResolver cannot be removed. + * + * @param beanNameResolver The BeanNameResolver to be registered. + */ + public void addBeanNameResolver(BeanNameResolver beanNameResolver) { + getELContext().addELResolver(new BeanNameELResolver(beanNameResolver)); + } + + /** + * Add an user defined ELResolver to the list of ELResolvers. Can be called multiple times. The new ELResolver is placed + * ahead of the default ELResolvers. The list of the ELResolvers added this way are ordered chronologically. + * + * @param elResolver The ELResolver to be added to the list of ELResolvers in ELContext. + * @see StandardELContext#addELResolver + */ + public void addELResolver(ELResolver elResolver) { + getELContext().addELResolver(elResolver); + } + + /** + * Maps a static method to Jakarta Expression Language function. + * + * @param prefix The namespace of the functions, can be "". + * @param function The name of the function. + * @param method The static method to be invoked when the function is used. + */ + public void mapFunction(String prefix, String function, Method method) { + getELContext().getFunctionMapper().mapFunction(prefix, function, method); + } + + /** + * Assign a ValueExpression to a Jakarta Expression Language variable, replacing any previous assignment to the same + * variable. The assignment for the variable is removed if the expression is <code>null</code>. + * + * @param variable The variable name + * @param expression The ValueExpression to be assigned to the variable. + */ + public void setVariable(String variable, ValueExpression expression) { + getELContext().getVariableMapper().setVariable(variable, expression); + } + + /** + * Import a static field or method. The class of the static member must be loadable from the classloader, at class + * resolution time. + * + * @param staticMemberName The full class name of the class to be imported + * @throws ELException if the name is not a full class name. + */ + public void importStatic(String staticMemberName) throws ELException { + getELContext().getImportHandler().importStatic(staticMemberName); + } + + /** + * Import a class. The imported class must be loadable from the classloader at the expression evaluation time. + * + * @param className The full class name of the class to be imported + * @throws ELException if the name is not a full class name. + */ + public void importClass(String className) throws ELException { + getELContext().getImportHandler().importClass(className); + } + + /** + * Import a package. At the expression evaluation time, the imported package name will be used to construct the full + * class name, which will then be used to load the class. Inherently, this is less efficient than importing a class. + * + * @param packageName The package name to be imported + */ + public void importPackage(String packageName) { + getELContext().getImportHandler().importPackage(packageName); + } + + /** + * Define a bean in the local bean repository + * + * @param name The name of the bean + * @param bean The bean instance to be defined. If null, the definition of the bean is removed. + * @return the previous bean (if any) mapped to <code>name</code> + */ + public Object defineBean(String name, Object bean) { + Object previousBean = getELContext().getBeans().get(name); + getELContext().getBeans().put(name, bean); + return previousBean; + } + + /** + * Register an evaluation listener. + * + * @param listener The evaluation listener to be added. + */ + public void addEvaluationListener(EvaluationListener listener) { + getELContext().addEvaluationListener(listener); + } +}
diff --git a/api/src/main/java/javax/el/ELProcessor.java b/api/src/main/java/javax/el/ELProcessor.java new file mode 100644 index 0000000..84edb6a --- /dev/null +++ b/api/src/main/java/javax/el/ELProcessor.java
@@ -0,0 +1,303 @@ +/* + * Copyright (c) 2012, 2018 Oracle and/or its affiliates and others. + * All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.el; + +import java.lang.reflect.Array; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +/** + * Provides an API for using Jakarta Expression Language in a stand-alone environment. + * + * <p> + * This class provides a direct and simple interface for + * <ul> + * <li>Evaluating Jakarta Expression Language expressions.</li> + * <li>Assigning values to beans or setting a bean property.</li> + * <li>Setting a {@link ValueExpression} to a Jakarta Expression Language variable.</li> + * <li>Defining a static method as Jakarta Expression Language function.</li> + * <li>Defining an object instance as Jakarta Expression Language name. + * </ul> + * + * <p> + * This API is not a replacement for the APIs in Jakarta Expression Language 2.2. Containers that maintain Jakarta + * Expression Language environments can continue to do so, without using this API. + * + * <p> + * For Jakarta Expression Language users who want to manipulate Jakarta Expression Language environments, like adding + * custom {@link ELResolver}s, {@link ELManager} can be used. + * + * <h3>Scope and Life Cycle</h3> + * <p> + * Since it maintains the state of the Jakarta Expression Language environments, <code>ELProcessor</code> is not thread + * safe. In the simplest case, an instance can be created and destroyed before and after evaluating Jakarta Expression + * Language expressions. A more general usage is to use an instance of <code>ELProcessor</code> for a session, so that + * the user can configure the Jakarta Expression Language evaluation environment for that session. + * </p> + * + * <h3>Automatic Bracketing of Expressions</h3> + * <p> + * A note about the Jakarta Expression Language expressions strings used in the class. The strings allowed in the methods + * {@link ELProcessor#getValue}, {@link ELProcessor#setValue}, and {@link ELProcessor#setVariable} are limited to + * non-composite expressions, i.e. expressions of the form ${...} or #{...} only. Also, it is not necessary (in fact not + * allowed) to bracket the expression strings with ${ or #{ and } in these methods: they will be automatically + * bracketed. This reduces the visual cluster, without any lost of functionalities (thanks to the addition of the + * concatenation operator). + * + * <h3>Example</h3> The following code snippet illustrates the use of ELProcessor to define a bean and evaluate its + * property. <blockquote> + * + * <pre> + * ELProcessor elp = new ELProcessor(); + * elp.defineBean("employee", new Employee("Charlie Brown")); + * String name = elp.eval("employee.name"); + * </pre> + * + * </blockquote> + * + * @since Jakarta Expression Language 3.0 + */ +public class ELProcessor { + + private ELManager elManager = new ELManager(); + private ExpressionFactory factory = ELManager.getExpressionFactory(); + + /** + * Return the ELManager used for Jakarta Expression Language processing. + * + * @return The ELManager used for Jakarta Expression Language processing. + */ + public ELManager getELManager() { + return elManager; + } + + /** + * Evaluates an Jakarta Expression Language expression. + * + * @param expression The Jakarta Expression Language expression to be evaluated. + * @return The result of the expression evaluation. + */ + public Object eval(String expression) { + return getValue(expression, Object.class); + } + + /** + * Evaluates an Jakarta Expression Language expression, and coerces the result to the specified type. + * + * @param expression The Jakarta Expression Language expression to be evaluated. + * @param expectedType Specifies the type that the resultant evaluation will be coerced to. + * @return The result of the expression evaluation. + */ + public Object getValue(String expression, Class<?> expectedType) { + ValueExpression exp = factory.createValueExpression(elManager.getELContext(), bracket(expression), expectedType); + return exp.getValue(elManager.getELContext()); + } + + /** + * Sets an expression with a new value. The target expression is evaluated, up to the last property resolution, and the + * resultant (base, property) pair is set to the provided value. + * + * @param expression The target expression + * @param value The new value to set. + * + * @throws PropertyNotFoundException if one of the property resolutions failed because a specified variable or property + * does not exist or is not readable. + * @throws PropertyNotWritableException if the final variable or property resolution failed because the specified + * variable or property is not writable. + * @throws ELException if an exception was thrown while attempting to set the property or variable. The thrown exception + * must be included as the cause property of this exception, if available. + */ + public void setValue(String expression, Object value) { + ValueExpression exp = factory.createValueExpression(elManager.getELContext(), bracket(expression), Object.class); + exp.setValue(elManager.getELContext(), value); + } + + /** + * Assign a Jakarta Expression Language expression to a Jakarta Expression Language variable. The expression is parsed, + * but not evaluated, and the parsed expression is mapped to the Jakarta Expression Language variable in the local + * variable map. Any previously assigned expression to the same variable will be replaced. If the expression is + * <code>null</code>, the variable will be removed. + * + * @param var The name of the variable. + * @param expression The Jakarta Expression Language expression to be assigned to the variable. + */ + public void setVariable(String var, String expression) { + ValueExpression exp = factory.createValueExpression(elManager.getELContext(), bracket(expression), Object.class); + elManager.setVariable(var, exp); + } + + /** + * Define a Jakarta Expression Language function in the local function mapper. + * + * @param prefix The namespace for the function or "" for no namesapce. + * @param function The name of the function. If empty (""), the method name is used as the function name. + * @param className The full Java class name that implements the function. + * @param method The name (specified without parenthesis) or the signature (as in the Java Language Spec) of the static + * method that implements the function. If the name (e.g. "sum") is given, the first declared method in class that + * matches the name is selected. If the signature (e.g. "int sum(int, int)" ) is given, then the declared method with + * the signature is selected. + * + * @throws NullPointerException if any of the arguments is null. + * @throws ClassNotFoundException if the specified class does not exists. + * @throws NoSuchMethodException if the method (with or without the signature) is not a declared method of the class, or + * if the method signature is not valid, or if the method is not a static method. + */ + public void defineFunction(String prefix, String function, String className, String method) throws ClassNotFoundException, NoSuchMethodException { + if (prefix == null || function == null || className == null || method == null) { + throw new NullPointerException("Null argument for defineFunction"); + } + + Method meth = null; + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + if (loader == null) { + loader = getClass().getClassLoader(); + } + + Class<?> klass = Class.forName(className, false, loader); + int j = method.indexOf('('); + if (j < 0) { + // Just a name is given + for (Method m : klass.getDeclaredMethods()) { + if (m.getName().equals(method)) { + meth = m; + } + } + if (meth == null) { + throw new NoSuchMethodException("Bad method name: " + method); + } + } else { + // method is the signature + // First get the method name, ignore the return type + int p = method.indexOf(' '); + if (p < 0) { + throw new NoSuchMethodException("Bad method signature: " + method); + } + + String methodName = method.substring(p + 1, j).trim(); + + // Extract parameter types + p = method.indexOf(')', j + 1); + if (p < 0) { + throw new NoSuchMethodException("Bad method signature: " + method); + } + String[] params = method.substring(j + 1, p).split(","); + Class<?>[] paramTypes = new Class<?>[params.length]; + for (int i = 0; i < params.length; i++) { + paramTypes[i] = toClass(params[i], loader); + } + meth = klass.getDeclaredMethod(methodName, paramTypes); + } + if (!Modifier.isStatic(meth.getModifiers())) { + throw new NoSuchMethodException("The method specified in defineFunction must be static: " + meth); + } + + if (function.equals("")) { + function = method; + } + + elManager.mapFunction(prefix, function, meth); + } + + /** + * Define a Jakarta Expression Language function in the local function mapper. + * + * @param prefix The namespace for the function or "" for no namesapce. + * @param function The name of the function. If empty (""), the method name is used as the function name. + * @param method The <code>java.lang.reflect.Method</code> instance of the method that implements the function. + * + * @throws NullPointerException if any of the arguments is null. + * @throws NoSuchMethodException if the method is not a static method + */ + public void defineFunction(String prefix, String function, Method method) throws NoSuchMethodException { + if (prefix == null || function == null || method == null) { + throw new NullPointerException("Null argument for defineFunction"); + } + if (!Modifier.isStatic(method.getModifiers())) { + throw new NoSuchMethodException("The method specified in defineFunction must be static: " + method); + } + if (function.equals("")) { + function = method.getName(); + } + + elManager.mapFunction(prefix, function, method); + } + + /** + * Define a bean in a local bean repository, hiding other beans of the same name. + * + * @param name The name of the bean + * @param bean The bean instance to be defined. If <code>null</code>, the name will be removed from the local bean + * repository. + */ + public void defineBean(String name, Object bean) { + elManager.defineBean(name, bean); + } + + /** + * Return the Class object associated with the class or interface with the given name. + */ + private static Class<?> toClass(String type, ClassLoader loader) throws ClassNotFoundException { + Class<?> c = null; + int i0 = type.indexOf('['); + int dims = 0; + if (i0 > 0) { + // This is an array. Count the dimensions + for (int i = 0; i < type.length(); i++) { + if (type.charAt(i) == '[') { + dims++; + } + } + type = type.substring(0, i0); + } + + if ("boolean".equals(type)) { + c = boolean.class; + } else if ("char".equals(type)) { + c = char.class; + } else if ("byte".equals(type)) { + c = byte.class; + } else if ("short".equals(type)) { + c = short.class; + } else if ("int".equals(type)) { + c = int.class; + } else if ("long".equals(type)) { + c = long.class; + } else if ("float".equals(type)) { + c = float.class; + } else if ("double".equals(type)) { + c = double.class; + } else { + c = loader.loadClass(type); + } + + if (dims == 0) { + return c; + } + + if (dims == 1) { + return java.lang.reflect.Array.newInstance(c, 1).getClass(); + } + + // Array of more than i dimension + return Array.newInstance(c, new int[dims]).getClass(); + } + + private String bracket(String expression) { + return "${" + expression + '}'; + } +}
diff --git a/api/src/main/java/javax/el/ELResolver.java b/api/src/main/java/javax/el/ELResolver.java new file mode 100644 index 0000000..f21459d --- /dev/null +++ b/api/src/main/java/javax/el/ELResolver.java
@@ -0,0 +1,332 @@ +/* + * Copyright (c) 1997-2018 Oracle and/or its affiliates and others. + * All rights reserved. + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.el; + +import java.beans.FeatureDescriptor; +import java.util.Iterator; + +/** + * Enables customization of variable, property, method call, and type conversion resolution behavior for Jakarta + * Expression Language expression evaluation. + * + * <p> + * While evaluating an expression, the <code>ELResolver</code> associated with the {@link ELContext} is consulted to do + * the initial resolution of the first variable of an expression. It is also consulted when a <code>.</code> or + * <code>[]</code> operator is encountered. + * + * <p> + * For example, in the Jakarta Expression Language expression <code>${employee.lastName}</code>, the + * <code>ELResolver</code> determines what object <code>employee</code> refers to, and what it means to get the + * <code>lastName</code> property on that object. + * + * <p> + * Most methods in this class accept a <code>base</code> and <code>property</code> parameter. In the case of variable + * resolution (e.g. determining what <code>employee</code> refers to in <code>${employee.lastName}</code>), the + * <code>base</code> parameter will be <code>null</code> and the <code>property</code> parameter will always be of type + * <code>String</code>. In this case, if the <code>property</code> is not a <code>String</code>, the behavior of the + * <code>ELResolver</code> is undefined. + * + * <p> + * In the case of property resolution, the <code>base</code> parameter identifies the base object and the + * <code>property</code> object identifies the property on that base. For example, in the expression + * <code>${employee.lastName}</code>, <code>base</code> is the result of the variable resolution for + * <code>employee</code> and <code>property</code> is the string <code>"lastName"</code>. In the expression + * <code>${y[x]}</code>, <code>base</code> is the result of the variable resolution for <code>y</code> and + * <code>property</code> is the result of the variable resolution for <code>x</code>. + * + * <p> + * In the case of method call resolution, the <code>base</code> parameter identifies the base object and the + * <code>method</code> parameter identifies a method on that base. In the case of overloaded methods, the <code> + * paramTypes</code> parameter can be optionally used to identify a method. The <code>params</code>parameter are the + * parameters for the method call, and can also be used for resolving overloaded methods when the + * <code>paramTypes</code> parameter is not specified. + * + * <p> + * In the case of type conversion resolution, the <code>obj</code> parameter identifies the source object and the + * <code>targetType</code> parameter identifies the target type the source to covert to. + * + * <p> + * Though only a single <code>ELResolver</code> is associated with an <code>ELContext</code>, there are usually multiple + * resolvers considered for any given variable or property resolution. <code>ELResolver</code>s are combined together + * using {@link CompositeELResolver}s, to define rich semantics for evaluating an expression. + * </p> + * + * <p> + * For the {@link #getValue}, {@link #getType}, {@link #setValue}, and {@link #isReadOnly} methods, an + * <code>ELResolver</code> is not responsible for resolving all possible (base, property) pairs. In fact, most resolvers + * will only handle a <code>base</code> of a single type. To indicate that a resolver has successfully resolved a + * particular (base, property) pair, it must set the <code>propertyResolved</code> property of the + * <code>ELContext</code> to <code>true</code>. If it could not handle the given pair, it must leave this property + * alone. The caller must ignore the return value of the method if <code>propertyResolved</code> is <code>false</code>. + * + * <p> + * Similarly, for the {@link #convertToType} method an <code>ELResolver</code> must set the + * <code>propertyResolved</code> to <code>true</code> to indicate that it handles the conversion of the object to the + * target type. + * + * <p> + * The {@link #getFeatureDescriptors} and {@link #getCommonPropertyType} methods are primarily designed for design-time + * tool support, but must handle invocation at runtime as well. The {@link java.beans.Beans#isDesignTime} method can be + * used to determine if the resolver is being consulted at design-time or runtime. + * + * @see CompositeELResolver + * @see ELContext#getELResolver + * @since Jakarta Server Pages 2.1 + */ +public abstract class ELResolver { + + // --------------------------------------------------------- Constants + + /** + * The attribute name of the named attribute in the <code>FeatureDescriptor</code> that specifies the runtime type of + * the variable or property. + */ + public static final String TYPE = "type"; + + /** + * The attribute name of the named attribute in the <code>FeatureDescriptor</code> that specifies whether the variable + * or property can be resolved at runtime. + */ + public static final String RESOLVABLE_AT_DESIGN_TIME = "resolvableAtDesignTime"; + + /** + * Attempts to resolve the given <code>property</code> object on the given <code>base</code> object. + * + * <p> + * If this resolver handles the given (base, property) pair, the <code>propertyResolved</code> property of the + * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is + * not <code>true</code> after this method is called, the caller should ignore the return value. + * </p> + * + * @param context The context of this evaluation. + * @param base The base object whose property value is to be returned, or <code>null</code> to resolve a top-level + * variable. + * @param property The property or variable to be resolved. + * + * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then + * the result of the variable or property resolution; otherwise undefined. + * + * @throws NullPointerException if context is <code>null</code> + * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but + * the specified variable or property does not exist or is not readable. + * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown + * exception must be included as the cause property of this exception, if available. + */ + public abstract Object getValue(ELContext context, Object base, Object property); + + /** + * Attempts to resolve and invoke the given <code>method</code> on the given <code>base</code> object. + * + * <p> + * If this resolver handles the given (base, method) pair, the <code>propertyResolved</code> property of the + * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is + * not <code>true</code> after this method is called, the caller should ignore the return value. + * + * <p> + * A default implementation is provided that returns null so that existing classes that extend ELResolver can continue + * to function. + * + * @param context The context of this evaluation. + * @param base The bean on which to invoke the method + * @param method The simple name of the method to invoke. Will be coerced to a <code>String</code>. + * @param paramTypes An array of Class objects identifying the method's formal parameter types, in declared order. Use + * an empty array if the method has no parameters. Can be <code>null</code>, in which case the method's formal parameter + * types are assumed to be unknown. + * @param params The parameters to pass to the method, or <code>null</code> if no parameters. + * + * @return The result of the method invocation (<code>null</code> if the method has a <code>void</code> return type). + * + * @throws MethodNotFoundException if no suitable method can be found. + * @throws ELException if an exception was thrown while performing (base, method) resolution. The thrown exception must + * be included as the cause property of this exception, if available. If the exception thrown is an + * <code>InvocationTargetException</code>, extract its <code>cause</code> and pass it to the <code>ELException</code> + * constructor. + * + * @since Jakarta Expression Language 2.2 + */ + public Object invoke(ELContext context, Object base, Object method, Class<?>[] paramTypes, Object[] params) { + return null; + } + + /** + * For a given <code>base</code> and <code>property</code>, attempts to identify the most general type that is + * acceptable for an object to be passed as the <code>value</code> parameter in a future call to the {@link #setValue} + * method. + * + * <p> + * If this resolver handles the given (base, property) pair, the <code>propertyResolved</code> property of the + * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is + * not <code>true</code> after this method is called, the caller should ignore the return value. + * </p> + * + * <p> + * This is not always the same as <code>getValue().getClass()</code>. For example, in the case of an + * {@link ArrayELResolver}, the <code>getType</code> method will return the element type of the array, which might be a + * superclass of the type of the actual element that is currently in the specified array element. + * </p> + * + * @param context The context of this evaluation. + * @param base The base object whose property value is to be analyzed, or <code>null</code> to analyze a top-level + * variable. + * @param property The property or variable to return the acceptable type for. + * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then + * the most general acceptable type; otherwise undefined. + * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but + * the specified variable or property does not exist or is not readable. + * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown + * exception must be included as the cause property of this exception, if available. + */ + public abstract Class<?> getType(ELContext context, Object base, Object property); + + /** + * Attempts to set the value of the given <code>property</code> object on the given <code>base</code> object. + * + * <p> + * If this resolver handles the given (base, property) pair, the <code>propertyResolved</code> property of the + * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is + * not <code>true</code> after this method is called, the caller can safely assume no value has been set. + * </p> + * + * @param context The context of this evaluation. + * @param base The base object whose property value is to be set, or <code>null</code> to set a top-level variable. + * @param property The property or variable to be set. + * @param value The value to set the property or variable to. + * @throws NullPointerException if context is <code>null</code> + * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but + * the specified variable or property does not exist. + * @throws PropertyNotWritableException if the given (base, property) pair is handled by this <code>ELResolver</code> + * but the specified variable or property is not writable. + * @throws ELException if an exception was thrown while attempting to set the property or variable. The thrown exception + * must be included as the cause property of this exception, if available. + */ + public abstract void setValue(ELContext context, Object base, Object property, Object value); + + /** + * For a given <code>base</code> and <code>property</code>, attempts to determine whether a call to {@link #setValue} + * will always fail. + * + * <p> + * If this resolver handles the given (base, property) pair, the <code>propertyResolved</code> property of the + * <code>ELContext</code> object must be set to <code>true</code> by the resolver, before returning. If this property is + * not <code>true</code> after this method is called, the caller should ignore the return value. + * </p> + * + * @param context The context of this evaluation. + * @param base The base object whose property value is to be analyzed, or <code>null</code> to analyze a top-level + * variable. + * @param property The property or variable to return the read-only status for. + * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then + * <code>true</code> if the property is read-only or <code>false</code> if not; otherwise undefined. + * @throws NullPointerException if context is <code>null</code> + * @throws PropertyNotFoundException if the given (base, property) pair is handled by this <code>ELResolver</code> but + * the specified variable or property does not exist. + * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown + * exception must be included as the cause property of this exception, if available. + */ + public abstract boolean isReadOnly(ELContext context, Object base, Object property); + + /** + * Returns information about the set of variables or properties that can be resolved for the given <code>base</code> + * object. One use for this method is to assist tools in auto-completion. + * + * <p> + * If the <code>base</code> parameter is <code>null</code>, the resolver must enumerate the list of top-level variables + * it can resolve. + * </p> + * + * <p> + * The <code>Iterator</code> returned must contain zero or more instances of {@link java.beans.FeatureDescriptor}, in no + * guaranteed order. In the case of primitive types such as <code>int</code>, the value <code>null</code> must be + * returned. This is to prevent the useless iteration through all possible primitive values. A return value of + * <code>null</code> indicates that this resolver does not handle the given <code>base</code> object or that the results + * are too complex to represent with this method and the {@link #getCommonPropertyType} method should be used instead. + * </p> + * + * <p> + * Each <code>FeatureDescriptor</code> will contain information about a single variable or property. In addition to the + * standard properties, the <code>FeatureDescriptor</code> must have two named attributes (as set by the + * <code>setValue</code> method): + * <ul> + * <li>{@link #TYPE} - The value of this named attribute must be an instance of <code>java.lang.Class</code> and specify + * the runtime type of the variable or property.</li> + * <li>{@link #RESOLVABLE_AT_DESIGN_TIME} - The value of this named attribute must be an instance of + * <code>java.lang.Boolean</code> and indicates whether it is safe to attempt to resolve this property at design-time. + * For instance, it may be unsafe to attempt a resolution at design time if the <code>ELResolver</code> needs access to + * a resource that is only available at runtime and no acceptable simulated value can be provided.</li> + * </ul> + * + * <p> + * The caller should be aware that the <code>Iterator</code> returned might iterate through a very large or even + * infinitely large set of properties. Care should be taken by the caller to not get stuck in an infinite loop. + * + * <p> + * This is a "best-effort" list. Not all <code>ELResolver</code>s will return completely accurate results, but all must + * be callable at both design-time and runtime (i.e. whether or not <code>Beans.isDesignTime()</code> returns + * <code>true</code>), without causing errors. + * + * <p> + * The <code>propertyResolved</code> property of the <code>ELContext</code> is not relevant to this method. The results + * of all <code>ELResolver</code>s are concatenated in the case of composite resolvers. + * + * @param context The context of this evaluation. + * @param base The base object whose set of valid properties is to be enumerated, or <code>null</code> to enumerate the + * set of top-level variables that this resolver can evaluate. + * @return An <code>Iterator</code> containing zero or more (possibly infinitely more) <code>FeatureDescriptor</code> + * objects, or <code>null</code> if this resolver does not handle the given <code>base</code> object or that the results + * are too complex to represent with this method + * @see java.beans.FeatureDescriptor + */ + public abstract Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base); + + /** + * Returns the most general type that this resolver accepts for the <code>property</code> argument, given a + * <code>base</code> object. One use for this method is to assist tools in auto-completion. + * + * <p> + * This assists tools in auto-completion and also provides a way to express that the resolver accepts a primitive value, + * such as an integer index into an array. For example, the {@link ArrayELResolver} will accept any <code>int</code> as + * a <code>property</code>, so the return value would be <code>Integer.class</code>. + * </p> + * + * @param context The context of this evaluation. + * @param base The base object to return the most general property type for, or <code>null</code> to enumerate the set + * of top-level variables that this resolver can evaluate. + * @return <code>null</code> if this <code>ELResolver</code> does not know how to handle the given <code>base</code> + * object; otherwise <code>Object.class</code> if any type of <code>property</code> is accepted; otherwise the most + * general <code>property</code> type accepted for the given <code>base</code>. + */ + public abstract Class<?> getCommonPropertyType(ELContext context, Object base); + + /** + * Converts an object to a specific type. + * + * <p> + * An <code>ELException</code> is thrown if an error occurs during the conversion. + * </p> + * + * @param context The context of this evaluation. + * @param obj The object to convert. + * @param targetType The target type for the conversion. + * @return object converted to <code>targetType</code> + * @throws ELException thrown if errors occur. + */ + public Object convertToType(ELContext context, Object obj, Class<?> targetType) { + return null; + } +}
diff --git a/api/src/main/java/javax/el/ELUtil.java b/api/src/main/java/javax/el/ELUtil.java new file mode 100644 index 0000000..4df4a44 --- /dev/null +++ b/api/src/main/java/javax/el/ELUtil.java
@@ -0,0 +1,794 @@ +/* + * Copyright (c) 1997-2018 Oracle and/or its affiliates and others. + * All rights reserved. + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.el; + +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * Utility methods for this portion of the Jakarta Expression Language implementation + * + * <p> + * Methods on this class use a Map instance stored in ThreadLocal storage to minimize the performance impact on + * operations that take place multiple times on a single Thread. The keys and values of the Map are implementation + * private. + * + * @author edburns + * @author Kin-man Chung + * @author Dongbin Nie + */ +class ELUtil { + + /** + * This class may not be constructed. + */ + private ELUtil() { + } + + /* + * For testing Backward Compatibility option static java.util.Properties properties = new java.util.Properties(); static + * { properties.setProperty("javax.el.bc2.2", "true"); } + */ + public static ExpressionFactory exprFactory = ExpressionFactory.newInstance(/* properties */); + + /** + * <p> + * The <code>ThreadLocal</code> variable used to record the <code>javax.faces.context.FacesContext</code> instance for + * each processing thread. + * </p> + */ + private static ThreadLocal<Map<String, ResourceBundle>> instance = new ThreadLocal<Map<String, ResourceBundle>>() { + @Override + protected Map<String, ResourceBundle> initialValue() { + return (null); + } + }; + + /** + * @return a Map stored in ThreadLocal storage. This may be used by methods of this class to minimize the performance + * impact for operations that may take place multiple times on a given Thread instance. + */ + private static Map<String, ResourceBundle> getCurrentInstance() { + Map<String, ResourceBundle> result = instance.get(); + if (result == null) { + result = new HashMap<>(); + setCurrentInstance(result); + } + + return result; + + } + + /** + * Replace the Map with the argument context. + * + * @param context the Map to be stored in ThreadLocal storage. + */ + private static void setCurrentInstance(Map<String, ResourceBundle> context) { + instance.set(context); + } + + /** + * Convenience method, calls through to getExceptionMessageString(javax.el.ELContext,java.lang.String,Object []). + * + * @param context the ELContext from which the Locale for this message is extracted. + * @param messageId the messageId String in the ResourceBundle + * + * @return a localized String for the argument messageId + */ + public static String getExceptionMessageString(ELContext context, String messageId) { + return getExceptionMessageString(context, messageId, null); + } + + /* + * <p>Return a Localized message String suitable for use as an Exception message. Examine the argument + * <code>context</code> for a <code>Locale</code>. If not present, use <code>Locale.getDefault()</code>. Load the + * <code>ResourceBundle</code> "javax.el.Messages" using that locale. Get the message string for argument + * <code>messageId</code>. If not found return "Missing Resource in Jakarta Expression Language implementation ??? messageId ???" with messageId + * substituted with the runtime value of argument <code>messageId</code>. If found, and argument <code>params</code> is + * non-null, format the message using the params. If formatting fails, return a sensible message including the + * <code>messageId</code>. If argument <code>params</code> is <code>null</code>, skip formatting and return the message + * directly, otherwise return the formatted message.</p> + * + * @param context the ELContext from which the Locale for this message is extracted. + * + * @param messageId the messageId String in the ResourceBundle + * + * @param params parameters to the message + * + * @return a localized String for the argument messageId + */ + public static String getExceptionMessageString(ELContext context, String messageId, Object[] params) { + String result = ""; + Locale locale = null; + + if (null == context || null == messageId) { + return result; + } + + if (null == (locale = context.getLocale())) { + locale = Locale.getDefault(); + } + + if (locale != null) { + Map<String, ResourceBundle> threadMap = getCurrentInstance(); + ResourceBundle resourceBundle = null; + if (null == (resourceBundle = threadMap.get(locale.toString()))) { + resourceBundle = ResourceBundle.getBundle("javax.el.PrivateMessages", locale); + threadMap.put(locale.toString(), resourceBundle); + } + + if (null != resourceBundle) { + try { + result = resourceBundle.getString(messageId); + if (null != params) { + result = MessageFormat.format(result, params); + } + } catch (IllegalArgumentException iae) { + result = "Can't get localized message: parameters to message appear to be incorrect. Message to format: " + messageId; + } catch (MissingResourceException mre) { + result = "Missing Resource in Jakarta Expression Language implementation: ???" + messageId + "???"; + } catch (Exception e) { + result = "Exception resolving message in Jakarta Expression Language implementation: ???" + messageId + "???"; + } + } + } + + return result; + } + + static ExpressionFactory getExpressionFactory() { + return exprFactory; + } + + static Constructor<?> findConstructor(Class<?> klass, Class<?>[] paramTypes, Object[] params) { + String methodName = "<init>"; + + if (klass == null) { + throw new MethodNotFoundException("Method not found: " + klass + "." + methodName + "(" + paramString(paramTypes) + ")"); + } + + if (paramTypes == null) { + paramTypes = getTypesFromValues(params); + } + + Constructor<?>[] constructors = klass.getConstructors(); + + List<Wrapper> wrappers = Wrapper.wrap(constructors); + + Wrapper result = findWrapper(klass, wrappers, methodName, paramTypes, params); + + if (result == null) { + return null; + } + + return getConstructor(klass, (Constructor<?>) result.unWrap()); + } + + static Object invokeConstructor(ELContext context, Constructor<?> constructor, Object[] params) { + Object[] parameters = buildParameters(context, constructor.getParameterTypes(), constructor.isVarArgs(), params); + try { + return constructor.newInstance(parameters); + } catch (IllegalAccessException iae) { + throw new ELException(iae); + } catch (IllegalArgumentException iae) { + throw new ELException(iae); + } catch (InvocationTargetException ite) { + throw new ELException(ite.getCause()); + } catch (InstantiationException ie) { + throw new ELException(ie.getCause()); + } + } + + static Method findMethod(Class<?> klass, String methodName, Class<?>[] paramTypes, Object[] params, boolean staticOnly) { + Method method = findMethod(klass, methodName, paramTypes, params); + if (staticOnly && !Modifier.isStatic(method.getModifiers())) { + throw new MethodNotFoundException("Method " + methodName + "for class " + klass + " not found or accessible"); + } + + return method; + } + + /* + * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync. + */ + static Object invokeMethod(ELContext context, Method method, Object base, Object[] params) { + + Object[] parameters = buildParameters(context, method.getParameterTypes(), method.isVarArgs(), params); + try { + return method.invoke(base, parameters); + } catch (IllegalAccessException iae) { + throw new ELException(iae); + } catch (IllegalArgumentException iae) { + throw new ELException(iae); + } catch (InvocationTargetException ite) { + throw new ELException(ite.getCause()); + } + } + + /* + * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync. + */ + static Method findMethod(Class<?> clazz, String methodName, Class<?>[] paramTypes, Object[] paramValues) { + if (clazz == null || methodName == null) { + throw new MethodNotFoundException("Method not found: " + clazz + "." + methodName + "(" + paramString(paramTypes) + ")"); + } + + if (paramTypes == null) { + paramTypes = getTypesFromValues(paramValues); + } + + Method[] methods = clazz.getMethods(); + + List<Wrapper> wrappers = Wrapper.wrap(methods, methodName); + + Wrapper result = findWrapper(clazz, wrappers, methodName, paramTypes, paramValues); + + if (result == null) { + return null; + } + + return getMethod(clazz, (Method) result.unWrap()); + } + + /* + * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync. + */ + @SuppressWarnings("null") + private static Wrapper findWrapper(Class<?> clazz, List<Wrapper> wrappers, String name, Class<?>[] paramTypes, Object[] paramValues) { + List<Wrapper> assignableCandidates = new ArrayList<>(); + List<Wrapper> coercibleCandidates = new ArrayList<>(); + List<Wrapper> varArgsCandidates = new ArrayList<>(); + + int paramCount; + if (paramTypes == null) { + paramCount = 0; + } else { + paramCount = paramTypes.length; + } + + for (Wrapper w : wrappers) { + Class<?>[] mParamTypes = w.getParameterTypes(); + int mParamCount; + if (mParamTypes == null) { + mParamCount = 0; + } else { + mParamCount = mParamTypes.length; + } + + // Check the number of parameters + if (!(paramCount == mParamCount || (w.isVarArgs() && paramCount >= mParamCount - 1))) { + // Method has wrong number of parameters + continue; + } + + // Check the parameters match + boolean assignable = false; + boolean coercible = false; + boolean varArgs = false; + boolean noMatch = false; + for (int i = 0; i < mParamCount; i++) { + if (i == (mParamCount - 1) && w.isVarArgs()) { + varArgs = true; + // exact var array type match + if (mParamCount == paramCount) { + if (mParamTypes[i] == paramTypes[i]) { + continue; + } + } + + // unwrap the array's component type + Class<?> varType = mParamTypes[i].getComponentType(); + for (int j = i; j < paramCount; j++) { + if (!isAssignableFrom(paramTypes[j], varType) + && !(paramValues != null && j < paramValues.length && isCoercibleFrom(paramValues[j], varType))) { + noMatch = true; + break; + } + } + } else if (mParamTypes[i].equals(paramTypes[i])) { + } else if (isAssignableFrom(paramTypes[i], mParamTypes[i])) { + assignable = true; + } else { + if (paramValues == null || i >= paramValues.length) { + noMatch = true; + break; + } else { + if (isCoercibleFrom(paramValues[i], mParamTypes[i])) { + coercible = true; + } else { + noMatch = true; + break; + } + } + } + } + if (noMatch) { + continue; + } + + if (varArgs) { + varArgsCandidates.add(w); + } else if (coercible) { + coercibleCandidates.add(w); + } else if (assignable) { + assignableCandidates.add(w); + } else { + // If a method is found where every parameter matches exactly, + // return it + return w; + } + + } + + String errorMsg = "Unable to find unambiguous method: " + clazz + "." + name + "(" + paramString(paramTypes) + ")"; + if (!assignableCandidates.isEmpty()) { + return findMostSpecificWrapper(assignableCandidates, paramTypes, false, errorMsg); + } else if (!coercibleCandidates.isEmpty()) { + return findMostSpecificWrapper(coercibleCandidates, paramTypes, true, errorMsg); + } else if (!varArgsCandidates.isEmpty()) { + return findMostSpecificWrapper(varArgsCandidates, paramTypes, true, errorMsg); + } else { + throw new MethodNotFoundException("Method not found: " + clazz + "." + name + "(" + paramString(paramTypes) + ")"); + } + + } + + /* + * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync. + */ + private static Wrapper findMostSpecificWrapper(List<Wrapper> candidates, Class<?>[] matchingTypes, boolean elSpecific, String errorMsg) { + List<Wrapper> ambiguouses = new ArrayList<>(); + for (Wrapper candidate : candidates) { + boolean lessSpecific = false; + + Iterator<Wrapper> it = ambiguouses.iterator(); + while (it.hasNext()) { + int result = isMoreSpecific(candidate, it.next(), matchingTypes, elSpecific); + if (result == 1) { + it.remove(); + } else if (result == -1) { + lessSpecific = true; + } + } + + if (!lessSpecific) { + ambiguouses.add(candidate); + } + } + + if (ambiguouses.size() > 1) { + throw new MethodNotFoundException(errorMsg); + } + + return ambiguouses.get(0); + } + + /* + * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync. + */ + private static int isMoreSpecific(Wrapper wrapper1, Wrapper wrapper2, Class<?>[] matchingTypes, boolean elSpecific) { + Class<?>[] paramTypes1 = wrapper1.getParameterTypes(); + Class<?>[] paramTypes2 = wrapper2.getParameterTypes(); + + if (wrapper1.isVarArgs()) { + // JLS8 15.12.2.5 Choosing the Most Specific Method + int length = Math.max(Math.max(paramTypes1.length, paramTypes2.length), matchingTypes.length); + paramTypes1 = getComparingParamTypesForVarArgsMethod(paramTypes1, length); + paramTypes2 = getComparingParamTypesForVarArgsMethod(paramTypes2, length); + + if (length > matchingTypes.length) { + Class<?>[] matchingTypes2 = new Class<?>[length]; + System.arraycopy(matchingTypes, 0, matchingTypes2, 0, matchingTypes.length); + matchingTypes = matchingTypes2; + } + } + + int result = 0; + for (int i = 0; i < paramTypes1.length; i++) { + if (paramTypes1[i] != paramTypes2[i]) { + int r2 = isMoreSpecific(paramTypes1[i], paramTypes2[i], matchingTypes[i], elSpecific); + if (r2 == 1) { + if (result == -1) { + return 0; + } + result = 1; + } else if (r2 == -1) { + if (result == 1) { + return 0; + } + result = -1; + } else { + return 0; + } + } + } + + if (result == 0) { + // The nature of bridge methods is such that it actually + // doesn't matter which one we pick as long as we pick + // one. That said, pick the 'right' one (the non-bridge + // one) anyway. + result = Boolean.compare(wrapper1.isBridge(), wrapper2.isBridge()); + } + + return result; + } + + /* + * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync. + */ + private static int isMoreSpecific(Class<?> type1, Class<?> type2, Class<?> matchingType, boolean elSpecific) { + type1 = getBoxingTypeIfPrimitive(type1); + type2 = getBoxingTypeIfPrimitive(type2); + if (type2.isAssignableFrom(type1)) { + return 1; + } else if (type1.isAssignableFrom(type2)) { + return -1; + } else { + if (elSpecific) { + /* + * Number will be treated as more specific + * + * ASTInteger only return Long or BigInteger, no Byte / Short / Integer. ASTFloatingPoint also. + * + */ + if (matchingType != null && Number.class.isAssignableFrom(matchingType)) { + boolean b1 = Number.class.isAssignableFrom(type1) || type1.isPrimitive(); + boolean b2 = Number.class.isAssignableFrom(type2) || type2.isPrimitive(); + if (b1 && !b2) { + return 1; + } else if (b2 && !b1) { + return -1; + } else { + return 0; + } + } + + return 0; + } else { + return 0; + } + } + } + + /* + * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync. + */ + private static Class<?> getBoxingTypeIfPrimitive(Class<?> clazz) { + if (clazz.isPrimitive()) { + if (clazz == Boolean.TYPE) { + return Boolean.class; + } + if (clazz == Character.TYPE) { + return Character.class; + } + if (clazz == Byte.TYPE) { + return Byte.class; + } + if (clazz == Short.TYPE) { + return Short.class; + } + if (clazz == Integer.TYPE) { + return Integer.class; + } + if (clazz == Long.TYPE) { + return Long.class; + } + if (clazz == Float.TYPE) { + return Float.class; + } + + return Double.class; + } else { + return clazz; + } + } + + /* + * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync. + */ + private static Class<?>[] getComparingParamTypesForVarArgsMethod(Class<?>[] paramTypes, int length) { + Class<?>[] result = new Class<?>[length]; + System.arraycopy(paramTypes, 0, result, 0, paramTypes.length - 1); + Class<?> type = paramTypes[paramTypes.length - 1].getComponentType(); + for (int i = paramTypes.length - 1; i < length; i++) { + result[i] = type; + } + + return result; + } + + /* + * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync. + */ + private static final String paramString(Class<?>[] types) { + if (types != null) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < types.length; i++) { + if (types[i] == null) { + sb.append("null, "); + } else { + sb.append(types[i].getName()).append(", "); + } + } + if (sb.length() > 2) { + sb.setLength(sb.length() - 2); + } + return sb.toString(); + } + return null; + } + + /* + * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync. + */ + static boolean isAssignableFrom(Class<?> src, Class<?> target) { + // src will always be an object + // Short-cut. null is always assignable to an object and in Jakarta Expression Language null + // can always be coerced to a valid value for a primitive + if (src == null) { + return true; + } + + target = getBoxingTypeIfPrimitive(target); + + return target.isAssignableFrom(src); + } + + /* + * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync. + */ + private static boolean isCoercibleFrom(Object src, Class<?> target) { + // TODO: This isn't pretty but it works. Significant refactoring would + // be required to avoid the exception. + try { + getExpressionFactory().coerceToType(src, target); + } catch (Exception e) { + return false; + } + + return true; + } + + /* + * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync. + */ + private static Class<?>[] getTypesFromValues(Object[] values) { + if (values == null) { + return null; + } + + Class<?> result[] = new Class<?>[values.length]; + for (int i = 0; i < values.length; i++) { + if (values[i] == null) { + result[i] = null; + } else { + result[i] = values[i].getClass(); + } + } + + return result; + } + + /* + * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync. + * + * Get a public method form a public class or interface of a given method. Note that if a PropertyDescriptor is obtained + * for a non-public class that implements a public interface, the read/write methods will be for the class, and + * therefore inaccessible. To correct this, a version of the same method must be found in a superclass or interface. + * + */ + static Method getMethod(Class<?> type, Method m) { + if (m == null || Modifier.isPublic(type.getModifiers())) { + return m; + } + Class<?>[] inf = type.getInterfaces(); + Method mp = null; + for (int i = 0; i < inf.length; i++) { + try { + mp = inf[i].getMethod(m.getName(), m.getParameterTypes()); + mp = getMethod(mp.getDeclaringClass(), mp); + if (mp != null) { + return mp; + } + } catch (NoSuchMethodException e) { + // Ignore + } + } + Class<?> sup = type.getSuperclass(); + if (sup != null) { + try { + mp = sup.getMethod(m.getName(), m.getParameterTypes()); + mp = getMethod(mp.getDeclaringClass(), mp); + if (mp != null) { + return mp; + } + } catch (NoSuchMethodException e) { + // Ignore + } + } + return null; + } + + /* + * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync. + */ + static Constructor<?> getConstructor(Class<?> type, Constructor<?> c) { + if (c == null || Modifier.isPublic(type.getModifiers())) { + return c; + } + Constructor<?> cp = null; + Class<?> sup = type.getSuperclass(); + if (sup != null) { + try { + cp = sup.getConstructor(c.getParameterTypes()); + cp = getConstructor(cp.getDeclaringClass(), cp); + if (cp != null) { + return cp; + } + } catch (NoSuchMethodException e) { + // Ignore + } + } + return null; + } + + /* + * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync. + */ + @SuppressWarnings("null") + static Object[] buildParameters(ELContext context, Class<?>[] parameterTypes, boolean isVarArgs, Object[] params) { + Object[] parameters = null; + if (parameterTypes.length > 0) { + parameters = new Object[parameterTypes.length]; + int paramCount = params == null ? 0 : params.length; + if (isVarArgs) { + int varArgIndex = parameterTypes.length - 1; + // First argCount-1 parameters are standard + for (int i = 0; (i < varArgIndex && i < paramCount); i++) { + parameters[i] = context.convertToType(params[i], parameterTypes[i]); + } + // Last parameter is the varargs + if (parameterTypes.length == paramCount && parameterTypes[varArgIndex] == params[varArgIndex].getClass()) { + parameters[varArgIndex] = params[varArgIndex]; + } else { + Class<?> varArgClass = parameterTypes[varArgIndex].getComponentType(); + final Object varargs = Array.newInstance(varArgClass, (paramCount - varArgIndex)); + for (int i = (varArgIndex); i < paramCount; i++) { + Array.set(varargs, i - varArgIndex, context.convertToType(params[i], varArgClass)); + } + parameters[varArgIndex] = varargs; + } + } else { + for (int i = 0; i < parameterTypes.length && i < paramCount; i++) { + parameters[i] = context.convertToType(params[i], parameterTypes[i]); + } + } + } + return parameters; + } + + /* + * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync. + */ + private abstract static class Wrapper { + + public static List<Wrapper> wrap(Method[] methods, String name) { + List<Wrapper> result = new ArrayList<>(); + for (Method method : methods) { + if (method.getName().equals(name)) { + result.add(new MethodWrapper(method)); + } + } + return result; + } + + public static List<Wrapper> wrap(Constructor<?>[] constructors) { + List<Wrapper> result = new ArrayList<>(); + for (Constructor<?> constructor : constructors) { + result.add(new ConstructorWrapper(constructor)); + } + return result; + } + + public abstract Object unWrap(); + + public abstract Class<?>[] getParameterTypes(); + + public abstract boolean isVarArgs(); + + public abstract boolean isBridge(); + } + + /* + * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync. + */ + private static class MethodWrapper extends Wrapper { + private final Method m; + + public MethodWrapper(Method m) { + this.m = m; + } + + @Override + public Object unWrap() { + return m; + } + + @Override + public Class<?>[] getParameterTypes() { + return m.getParameterTypes(); + } + + @Override + public boolean isVarArgs() { + return m.isVarArgs(); + } + + @Override + public boolean isBridge() { + return m.isBridge(); + } + } + + /* + * This method duplicates code in com.sun.el.util.ReflectionUtil. When making changes keep the code in sync. + */ + private static class ConstructorWrapper extends Wrapper { + private final Constructor<?> c; + + public ConstructorWrapper(Constructor<?> c) { + this.c = c; + } + + @Override + public Object unWrap() { + return c; + } + + @Override + public Class<?>[] getParameterTypes() { + return c.getParameterTypes(); + } + + @Override + public boolean isVarArgs() { + return c.isVarArgs(); + } + + @Override + public boolean isBridge() { + return false; + } + } + +}
diff --git a/api/src/main/java/javax/el/EvaluationListener.java b/api/src/main/java/javax/el/EvaluationListener.java new file mode 100644 index 0000000..f359ad2 --- /dev/null +++ b/api/src/main/java/javax/el/EvaluationListener.java
@@ -0,0 +1,55 @@ +/* + * Copyright (c) 2012, 2018 Oracle and/or its affiliates and others. + * All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.el; + +/** + * The listener interface for receiving notification when a Jakarta Expression Language expression is evaluated. + * + * @since Jakarta Expression Language 3.0 + */ +public abstract class EvaluationListener { + + /** + * Receives notification before a Jakarta Expression Language expression is evaluated + * + * @param context The ELContext + * @param expression The Jakarta Expression Language expression string to be evaluated + */ + public void beforeEvaluation(ELContext context, String expression) { + } + + /** + * Receives notification after a Jakarta Expression Language expression is evaluated + * + * @param context The ELContext + * @param expression The Jakarta Expression Language expression string to be evaluated + */ + public void afterEvaluation(ELContext context, String expression) { + } + + /** + * Receives notification when the (base, property) pair is resolved + * + * @param context The ELContext + * @param base The base object + * @param property The property object + */ + public void propertyResolved(ELContext context, Object base, Object property) { + } + +}
diff --git a/api/src/main/java/javax/el/Expression.java b/api/src/main/java/javax/el/Expression.java new file mode 100644 index 0000000..fd00f8f --- /dev/null +++ b/api/src/main/java/javax/el/Expression.java
@@ -0,0 +1,119 @@ +/* + * Copyright (c) 1997-2018 Oracle and/or its affiliates and others. + * All rights reserved. + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.el; + +import java.io.Serializable; + +/** + * Base class for the expression subclasses {@link ValueExpression} and {@link MethodExpression}, implementing + * characteristics common to both. + * + * <p> + * All expressions must implement the <code>equals()</code> and <code>hashCode()</code> methods so that two expressions + * can be compared for equality. They are redefined abstract in this class to force their implementation in subclasses. + * </p> + * + * <p> + * All expressions must also be <code>Serializable</code> so that they can be saved and restored. + * </p> + * + * <p> + * <code>Expression</code>s are also designed to be immutable so that only one instance needs to be created for any + * given expression String / {@link FunctionMapper}. This allows a container to pre-create expressions and not have to + * re-parse them each time they are evaluated. + * </p> + * + * @since Jakarta Server Pages 2.1 + */ +public abstract class Expression implements Serializable { + + private static final long serialVersionUID = -6663767980471823812L; + + /** + * Returns the original String used to create this <code>Expression</code>, unmodified. + * + * <p> + * This is used for debugging purposes but also for the purposes of comparison (e.g. to ensure the expression in a + * configuration file has not changed). + * </p> + * + * <p> + * This method does not provide sufficient information to re-create an expression. Two different expressions can have + * exactly the same expression string but different function mappings. Serialization should be used to save and restore + * the state of an <code>Expression</code>. + * </p> + * + * @return The original expression String. + */ + public abstract String getExpressionString(); + + // Comparison + + /** + * Determines whether the specified object is equal to this <code>Expression</code>. + * + * <p> + * The result is <code>true</code> if and only if the argument is not <code>null</code>, is an <code>Expression</code> + * object that is the of the same type (<code>ValueExpression</code> or <code>MethodExpression</code>), and has an + * identical parsed representation. + * </p> + * + * <p> + * Note that two expressions can be equal if their expression Strings are different. For example, + * <code>${fn1:foo()}</code> and <code>${fn2:foo()}</code> are equal if their corresponding <code>FunctionMapper</code>s + * mapped <code>fn1:foo</code> and <code>fn2:foo</code> to the same method. + * </p> + * + * @param obj the <code>Object</code> to test for equality. + * @return <code>true</code> if <code>obj</code> equals this <code>Expression</code>; <code>false</code> otherwise. + * @see java.util.Hashtable + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public abstract boolean equals(Object obj); + + /** + * Returns the hash code for this <code>Expression</code>. + * + * <p> + * See the note in the {@link #equals} method on how two expressions can be equal if their expression Strings are + * different. Recall that if two objects are equal according to the <code>equals(Object)</code> method, then calling the + * <code>hashCode</code> method on each of the two objects must produce the same integer result. Implementations must + * take special note and implement <code>hashCode</code> correctly. + * </p> + * + * @return The hash code for this <code>Expression</code>. + * @see #equals + * @see java.util.Hashtable + * @see java.lang.Object#hashCode() + */ + @Override + public abstract int hashCode(); + + /** + * Returns whether this expression was created from only literal text. + * + * <p> + * This method must return <code>true</code> if and only if the expression string this expression was created from + * contained no unescaped Jakarta Expression Language delimeters (<code>${...}</code> or <code>#{...}</code>). + * + * @return <code>true</code> if this expression was created from only literal text; <code>false</code> otherwise. + */ + public abstract boolean isLiteralText(); +}
diff --git a/api/src/main/java/javax/el/ExpressionFactory.java b/api/src/main/java/javax/el/ExpressionFactory.java new file mode 100644 index 0000000..e005de4 --- /dev/null +++ b/api/src/main/java/javax/el/ExpressionFactory.java
@@ -0,0 +1,270 @@ +/* + * Copyright (c) 1997-2018 Oracle and/or its affiliates and others. + * All rights reserved. + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.el; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.Properties; + +/** + * Provides an implementation for creating and evaluating Jakarta Expression Language expressions. + * + * <p> + * Classes that implement the Jakarta Expression Language expression language expose their functionality via this + * abstract class. An implementation supports the following functionalities. + * + * <ul> + * <li>Parses a <code>String</code> into a {@link ValueExpression} or {@link MethodExpression} instance for later + * evaluation.</li> + * <li>Implements an <code>ELResolver</code> for query operators</li> + * <li>Provides a default type coercion</li> + * </ul> + * + * <p> + * The {@link #newInstance} method can be used to obtain an instance of the implementation. Technologies such as + * Jakarta Server Pages and Jakarta Faces provide access to an implementation via factory methods. + * + * <p> + * The {@link #createValueExpression} method is used to parse expressions that evaluate to values (both l-values and + * r-values are supported). The {@link #createMethodExpression} method is used to parse expressions that evaluate to a + * reference to a method on an object. + * + * <p> + * Resolution of model objects is performed at evaluation time, via the {@link ELResolver} associated with the + * {@link ELContext} passed to the <code>ValueExpression</code> or <code>MethodExpression</code>. + * + * <p> + * The ELContext object also provides access to the {@link FunctionMapper} and {@link VariableMapper} to be used when + * parsing the expression. Jakarta Expression Language function and variable mapping is performed at parse-time, and the + * results are bound to the expression. Therefore, the {@link ELContext}, {@link FunctionMapper}, and + * {@link VariableMapper} are not stored for future use and do not have to be <code>Serializable</code>. + * + * <p> + * The <code>createValueExpression</code> and <code>createMethodExpression</code> methods must be thread-safe. That is, + * multiple threads may call these methods on the same <code>ExpressionFactory</code> object simultaneously. + * Implementations should synchronize access if they depend on transient state. Implementations should not, however, + * assume that only one object of each <code>ExpressionFactory</code> type will be instantiated; global caching should + * therefore be static. + * + * <p> + * The <code>ExpressionFactory</code> must be able to handle the following types of input for the + * <code>expression</code> parameter: + * <ul> + * <li>Single expressions using the <code>${}</code> delimiter (e.g. <code>"${employee.lastName}"</code>).</li> + * <li>Single expressions using the <code>#{}</code> delimiter (e.g. <code>"#{employee.lastName}"</code>).</li> + * <li>Literal text containing no <code>${}</code> or <code>#{}</code> delimiters (e.g. <code>"John Doe"</code>).</li> + * <li>Multiple expressions using the same delimiter (e.g. <code>"${employee.firstName}${employee.lastName}"</code> or + * <code>"#{employee.firstName}#{employee.lastName}"</code>).</li> + * <li>Mixed literal text and expressions using the same delimiter (e.g. + * <code>"Name: ${employee.firstName} ${employee.lastName}"</code>).</li> + * </ul> + * + * <p> + * The following types of input are illegal and must cause an {@link ELException} to be thrown: + * <ul> + * <li>Multiple expressions using different delimiters (e.g. + * <code>"${employee.firstName}#{employee.lastName}"</code>).</li> + * <li>Mixed literal text and expressions using different delimiters(e.g. + * <code>"Name: ${employee.firstName} #{employee.lastName}"</code>).</li> + * </ul> + * + * @since Jakarta Server Pages 2.1 + */ +public abstract class ExpressionFactory { + + /** + * Creates a new instance of a <code>ExpressionFactory</code>. This method uses the following ordered lookup procedure + * to determine the <code>ExpressionFactory</code> implementation class to load: + * + * <ul> + * <li>Use the Services API (as detailed in the JAR specification). If a resource with the name of + * <code>META-INF/services/javax.el.ExpressionFactory</code> exists, then its first line, if present, is used as the + * UTF-8 encoded name of the implementation class.</li> + * <li>Use the properties file "lib/el.properties" in the JRE directory. If this file exists and it is readable by the + * <code> java.util.Properties.load(InputStream)</code> method, and it contains an entry whose key is + * "javax.el.ExpressionFactory", then the value of that entry is used as the name of the implementation class.</li> + * <li>Use the <code>javax.el.ExpressionFactory</code> system property. If a system property with this name is defined, + * then its value is used as the name of the implementation class.</li> + * <li>Use a platform default implementation.</li> + * </ul> + * + * @return a new <code>ExpressionFactory</code> instance + */ + public static ExpressionFactory newInstance() { + return ExpressionFactory.newInstance(null); + } + + /** + * Create a new instance of a <code>ExpressionFactory</code>, with optional properties. + * + * <p> + * This method uses the same lookup procedure as the one used in <code>newInstance()</code>. + * + * <p> + * If the argument <code>properties</code> is not null, and if the implementation contains a constructor with a single + * parameter of type <code>java.util.Properties</code>, then the constructor is used to create the instance. + * + * <p> + * Properties are optional and can be ignored by an implementation. + * + * <p> + * The name of a property should start with "javax.el." + * + * <p> + * The following are some suggested names for properties. + * <ul> + * <li>javax.el.cacheSize</li> + * </ul> + * + * @param properties Properties passed to the implementation. If null, then no properties. + * + * @return a new <code>ExpressionFactory</code> instance + */ + public static ExpressionFactory newInstance(Properties properties) { + return (ExpressionFactory) FactoryFinder.find("javax.el.ExpressionFactory", "com.sun.el.ExpressionFactoryImpl", properties); + } + + /** + * Parses an expression into a {@link ValueExpression} for later evaluation. Use this method for expressions that refer + * to values. + * + * <p> + * This method should perform syntactic validation of the expression. If in doing so it detects errors, it should raise + * an <code>ELException</code>. + * + * @param context The Jakarta Expression Language context used to parse the expression. The <code>FunctionMapper</code> + * and <code>VariableMapper</code> stored in the ELContext are used to resolve functions and variables found in the + * expression. They can be <code>null</code>, in which case functions or variables are not supported for this + * expression. The object returned must invoke the same functions and access the same variable mappings regardless of + * whether the mappings in the provided <code>FunctionMapper</code> and <code>VariableMapper</code> instances change + * between calling <code>ExpressionFactory.createValueExpression()</code> and any method on + * <code>ValueExpression</code>. Note that within Jakarta Expression Language, the ${} and #{} syntaxes are treated + * identically. This includes the use of VariableMapper and FunctionMapper at expression creation time. Each is invoked + * if not null, independent of whether the #{} or ${} syntax is used for the expression. + * @param expression The expression to parse + * @param expectedType The type the result of the expression will be coerced to after evaluation. + * + * @return The parsed expression + * + * @throws NullPointerException Thrown if expectedType is null. + * @throws ELException Thrown if there are syntactical errors in the provided expression. + */ + public abstract ValueExpression createValueExpression(ELContext context, String expression, Class<?> expectedType); + + /** + * Creates a ValueExpression that wraps an object instance. + * + * <p> + * This method can be used to pass any object as a ValueExpression. The wrapper ValueExpression is read only, and + * returns the wrapped object via its <code>getValue()</code> method, optionally coerced. + * </p> + * + * @param instance The object instance to be wrapped. + * @param expectedType The type the result of the expression will be coerced to after evaluation. There will be no + * coercion if it is Object.class, + * @throws NullPointerException Thrown if expectedType is null. + * @return a ValueExpression that wraps an object instance + */ + public abstract ValueExpression createValueExpression(Object instance, Class<?> expectedType); + + /** + * Parses an expression into a {@link MethodExpression} for later evaluation. Use this method for expressions that refer + * to methods. + * + * <p> + * If the expression is a String literal, a <code>MethodExpression + * </code> is created, which when invoked, returns the String literal, coerced to expectedReturnType. An ELException is + * thrown if expectedReturnType is void or if the coercion of the String literal to the expectedReturnType yields an + * error (see Section "1.16 Type Conversion"). + * + * <p> + * This method should perform syntactic validation of the expression. If in doing so it detects errors, it should raise + * an <code>ELException</code>. + * + * @param context The Jakarta Expression Language context used to parse the expression. The <code>FunctionMapper</code> + * and <code>VariableMapper</code> stored in the ELContext are used to resolve functions and variables found in the + * expression. They can be <code>null</code>, in which case functions or variables are not supported for this + * expression. The object returned must invoke the same functions and access the same variable mappings regardless of + * whether the mappings in the provided <code>FunctionMapper</code> and <code>VariableMapper</code> instances change + * between calling <code>ExpressionFactory.createMethodExpression()</code> and any method on + * <code>MethodExpression</code>. Note that within the EL, the ${} and #{} syntaxes are treated identically. This + * includes the use of VariableMapper and FunctionMapper at expression creation time. Each is invoked if not null, + * independent of whether the #{} or ${} syntax is used for the expression. + * @param expression The expression to parse + * @param expectedReturnType The expected return type for the method to be found. After evaluating the expression, the + * <code>MethodExpression</code> must check that the return type of the actual method matches this type. Passing in a + * value of <code>null</code> indicates the caller does not care what the return type is, and the check is disabled. + * @param expectedParamTypes The expected parameter types for the method to be found. Must be an array with no elements + * if there are no parameters expected. It is illegal to pass <code>null</code>, unless the method is specified with + * arguments in the Jakarta Expression Language expression, in which case these arguments are used for method selection, + * and this parameter is ignored. + * + * @return The parsed expression + * + * @throws ELException Thrown if there are syntactical errors in the provided expression. + * @throws NullPointerException if paramTypes is <code>null</code>. + */ + public abstract MethodExpression createMethodExpression(ELContext context, String expression, Class<?> expectedReturnType, Class<?>[] expectedParamTypes); + + /** + * Coerces an object to a specific type according to the Jakarta Expression Language type conversion rules. The custom + * type conversions in the <code>ELResolver</code>s are not considered. + * + * <p> + * An <code>ELException</code> is thrown if an error results from applying the conversion rules. + * + * @param obj The object to coerce. + * @param targetType The target type for the coercion. + * + * @return an object coerced to <code>targetType</code> + * + * @throws ELException thrown if an error results from applying the conversion rules. + */ + public abstract Object coerceToType(Object obj, Class<?> targetType); + + /** + * Retrieves an ELResolver that implements the operations in collections. + * + * <p> + * This ELResolver resolves the method invocation on the pair (<code>base</code>, <code>property</code>) when + * <code>base</code> is a <code>Collection</code> or a <code>Map</code>, and <code>property</code> is the name of the + * operation. + * + * <p> + * See the specification document for detailed descriptions of these operators, their arguments, and return values. + * + * @return The <code>ELResolver</code> that implements the Query Operators. + * + * @since Jakarta Expression Language 3.0 + */ + public ELResolver getStreamELResolver() { + return null; + } + + /** + * Retrieve a function map containing a pre-configured function mapping. + * + * @return A initial map for functions, null if there is none. + * + * @since Jakarta Expression Language 3.0 + */ + public Map<String, Method> getInitFunctionMap() { + return null; + } +}
diff --git a/api/src/main/java/javax/el/FactoryFinder.java b/api/src/main/java/javax/el/FactoryFinder.java new file mode 100644 index 0000000..273ab22 --- /dev/null +++ b/api/src/main/java/javax/el/FactoryFinder.java
@@ -0,0 +1,143 @@ +/* + * Copyright (c) 1997-2018 Oracle and/or its affiliates and others. + * All rights reserved. + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.el; + +import static java.io.File.separator; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Constructor; +import java.util.Properties; + +class FactoryFinder { + + /** + * Creates an instance of the specified class using the specified <code>ClassLoader</code> object. + * + * @exception ELException if the given class could not be found or could not be instantiated + */ + private static Object newInstance(String className, ClassLoader classLoader, Properties properties) { + try { + Class<?> spiClass; + if (classLoader == null) { + spiClass = Class.forName(className); + } else { + spiClass = classLoader.loadClass(className); + } + + if (properties != null) { + Constructor<?> constr = null; + try { + constr = spiClass.getConstructor(Properties.class); + } catch (Exception ex) { + } + + if (constr != null) { + return constr.newInstance(properties); + } + } + return spiClass.newInstance(); + } catch (ClassNotFoundException x) { + throw new ELException("Provider " + className + " not found", x); + } catch (Exception x) { + throw new ELException("Provider " + className + " could not be instantiated: " + x, x); + } + } + + /** + * Finds the implementation <code>Class</code> object for the given factory name, or if that fails, finds the + * <code>Class</code> object for the given fallback class name. The arguments supplied must be used in order. If using + * the first argument is successful, the second one will not be used. + * <P> + * This method is package private so that this code can be shared. + * + * @return the <code>Class</code> object of the specified message factory; may not be <code>null</code> + * + * @param factoryId the name of the factory to find, which is a system property + * @param fallbackClassName the implementation class name, which is to be used only if nothing else is found; + * <code>null</code> to indicate that there is no fallback class name + * @exception ELException if there is an error + */ + static Object find(String factoryId, String fallbackClassName, Properties properties) { + ClassLoader classLoader; + try { + classLoader = Thread.currentThread().getContextClassLoader(); + } catch (Exception x) { + throw new ELException(x.toString(), x); + } + + String serviceId = "META-INF/services/" + factoryId; + + // try to find services in CLASSPATH + try { + InputStream is = null; + if (classLoader == null) { + is = ClassLoader.getSystemResourceAsStream(serviceId); + } else { + is = classLoader.getResourceAsStream(serviceId); + } + + if (is != null) { + BufferedReader reader = new BufferedReader(new InputStreamReader(is, "UTF-8")); + + String factoryClassName = reader.readLine(); + reader.close(); + + if (factoryClassName != null && !"".equals(factoryClassName)) { + return newInstance(factoryClassName, classLoader, properties); + } + } + } catch (Exception ex) { + } + + // Try to read from $java.home/lib/el.properties + try { + String javah = System.getProperty("java.home"); + String configFileName = javah + separator + "lib" + separator + "el.properties"; + + File configFile = new File(configFileName); + if (configFile.exists()) { + Properties props = new Properties(); + props.load(new FileInputStream(configFile)); + String factoryClassName = props.getProperty(factoryId); + + return newInstance(factoryClassName, classLoader, properties); + } + } catch (Exception ex) { + } + + // Use the system property + try { + String systemProp = System.getProperty(factoryId); + if (systemProp != null) { + return newInstance(systemProp, classLoader, properties); + } + } catch (SecurityException se) { + } + + if (fallbackClassName == null) { + throw new ELException("Provider for " + factoryId + " cannot be found", null); + } + + return newInstance(fallbackClassName, classLoader, properties); + } +}
diff --git a/api/src/main/java/javax/el/FunctionMapper.java b/api/src/main/java/javax/el/FunctionMapper.java new file mode 100644 index 0000000..36e76a7 --- /dev/null +++ b/api/src/main/java/javax/el/FunctionMapper.java
@@ -0,0 +1,61 @@ +/* + * Copyright (c) 1997-2018 Oracle and/or its affiliates and others. + * All rights reserved. + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.el; + +import java.lang.reflect.Method; + +/** + * The interface to a map between Jakarta Expression Language function names and methods. + * + * <p> + * A <code>FunctionMapper</code> maps <code>${prefix:name()}</code> style functions to a static method that can execute + * that function. + * + * @since Jakarta Server Pages 2.1 + */ +public abstract class FunctionMapper { + + /** + * Resolves the specified prefix and local name into a <code>java.lang.Method</code>. + * + * <p> + * Returns <code>null</code> if no function could be found that matches the given prefix and local name. + * </p> + * + * @param prefix the prefix of the function, or "" if no prefix. For example, <code>"fn"</code> in + * <code>${fn:method()}</code>, or <code>""</code> in <code>${method()}</code>. + * @param localName the short name of the function. For example, <code>"method"</code> in <code>${fn:method()}</code>. + * @return the static method to invoke, or <code>null</code> if no match was found. + */ + public abstract Method resolveFunction(String prefix, String localName); + + /** + * Adds a static method that can be used as a function. + * + * @param prefix the prefix of the function, or "" if no prefix. For example, <code>"fn"</code> in + * <code>${fn:method()}</code>, or <code>""</code> in <code>${method()}</code>. + * @param localName the short name of the function. For example, <code>"method"</code> in <code>${fn:method()}</code>. + * @param meth The static method that is to be invoked, when the function is referenced. The null value causes the + * function to be removed from the map. + * + * @since Jakarta Expression Language 3.0 + */ + public void mapFunction(String prefix, String localName, Method meth) { + } +}
diff --git a/api/src/main/java/javax/el/ImportHandler.java b/api/src/main/java/javax/el/ImportHandler.java new file mode 100644 index 0000000..f81866e --- /dev/null +++ b/api/src/main/java/javax/el/ImportHandler.java
@@ -0,0 +1,169 @@ +/* + * Copyright (c) 2012, 2018 Oracle and/or its affiliates and others. + * All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.el; + +import static java.lang.reflect.Modifier.isAbstract; +import static java.lang.reflect.Modifier.isInterface; +import static java.lang.reflect.Modifier.isPublic; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +/** + * Handles imports of class names and package names. An imported package name implicitly imports all the classes in the + * package. A class that has been imported can be used without its package name. The name is resolved to its full + * (package and class) name at evaluation time. + */ +public class ImportHandler { + + private Map<String, String> classNameMap = new HashMap<>(); + private Map<String, Class<?>> classMap = new HashMap<>(); + private Map<String, String> staticNameMap = new HashMap<>(); + private HashSet<String> notAClass = new HashSet<>(); + private List<String> packages = new ArrayList<>(); + + { + importPackage("java.lang"); + } + + /** + * Import a static field or method. + * + * @param name The static member name, including the full class name, to be imported + * @throws ELException if the name does not include a ".". + */ + public void importStatic(String name) throws ELException { + int i = name.lastIndexOf('.'); + if (i <= 0) { + throw new ELException("The name " + name + " is not a full static member name"); + } + + String memberName = name.substring(i + 1); + String className = name.substring(0, i); + + staticNameMap.put(memberName, className); + } + + /** + * Import a class. + * + * @param name The full class name of the class to be imported + * @throws ELException if the name does not include a ".". + */ + public void importClass(String name) throws ELException { + int i = name.lastIndexOf('.'); + if (i <= 0) { + throw new ELException("The name " + name + " is not a full class name"); + } + + String className = name.substring(i + 1); + + classNameMap.put(className, name); + } + + /** + * Import all the classes in a package. + * + * @param packageName The package name to be imported + */ + public void importPackage(String packageName) { + packages.add(packageName); + } + + /** + * Resolve a class name. + * + * @param name The name of the class (without package name) to be resolved. + * @return If the class has been imported previously, with {@link #importClass} or {@link #importPackage}, then its + * Class instance. Otherwise <code>null</code>. + * @throws ELException if the class is abstract or is an interface, or not public. + */ + public Class<?> resolveClass(String name) { + String className = classNameMap.get(name); + if (className != null) { + return resolveClassFor(className); + } + + for (String packageName : packages) { + String fullClassName = packageName + "." + name; + Class<?> c = resolveClassFor(fullClassName); + if (c != null) { + classNameMap.put(name, fullClassName); + return c; + } + } + + return null; + } + + /** + * Resolve a static field or method name. + * + * @param name The name of the member(without package and class name) to be resolved. + * @return If the field or method has been imported previously, with {@link #importStatic}, then the class object + * representing the class that declares the static field or method. Otherwise <code>null</code>. + * @throws ELException if the class is not public, or is abstract or is an interface. + */ + public Class<?> resolveStatic(String name) { + String className = staticNameMap.get(name); + if (className != null) { + Class<?> c = resolveClassFor(className); + if (c != null) { + return c; + } + } + + return null; + } + + private Class<?> resolveClassFor(String className) { + Class<?> c = classMap.get(className); + if (c != null) { + return c; + } + + c = getClassFor(className); + if (c != null) { + checkModifiers(c.getModifiers()); + classMap.put(className, c); + } + + return c; + } + + private Class<?> getClassFor(String className) { + if (!notAClass.contains(className)) { + try { + return Class.forName(className, false, Thread.currentThread().getContextClassLoader()); + } catch (ClassNotFoundException ex) { + notAClass.add(className); + } + } + + return null; + } + + private void checkModifiers(int modifiers) { + if (isAbstract(modifiers) || isInterface(modifiers) || !isPublic((modifiers))) { + throw new ELException("Imported class must be public, and cannot be abstract or an interface"); + } + } +}
diff --git a/api/src/main/java/javax/el/LambdaExpression.java b/api/src/main/java/javax/el/LambdaExpression.java new file mode 100644 index 0000000..540be11 --- /dev/null +++ b/api/src/main/java/javax/el/LambdaExpression.java
@@ -0,0 +1,154 @@ +/* + * Copyright (c) 2012, 2018 Oracle and/or its affiliates and others. + * All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.el; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Encapsulates a parameterized {@link ValueExpression}. + * + * <p> + * A <code>LambdaExpression</code> is a representation of the Jakarta Expression Language Lambda expression syntax. It + * consists of a list of the formal parameters and a body, represented by a {@link ValueExpression}. The body can be any + * valid <code>Expression</code>, including another <code>LambdaExpression</code>. + * + * <p> + * A <code>LambdaExpression</code> is created when an Jakarta Expression Language expression containing a Lambda + * expression is evaluated. + * + * <p> + * A <code>LambdaExpression</code> can be invoked by calling {@link LambdaExpression#invoke}, with an + * {@link javax.el.ELContext} and a list of the actual arguments. Alternately, a <code>LambdaExpression</code> can be + * invoked without passing a <code>ELContext</code>, in which case the <code>ELContext</code> previously set by calling + * {@link LambdaExpression#setELContext} will be used. The evaluation of the <code>ValueExpression</code> in the body + * uses the {@link ELContext} to resolve references to the parameters, and to evaluate the lambda expression. The result + * of the evaluation is returned. + * + * @see ELContext#getLambdaArgument + * @see ELContext#enterLambdaScope + * @see ELContext#exitLambdaScope + */ +public class LambdaExpression { + + private List<String> formalParameters = new ArrayList<>(); + private ValueExpression expression; + private ELContext context; + // Arguments from nesting lambdas, when the body is another lambda + private Map<String, Object> envirArgs; + + /** + * Creates a new LambdaExpression. + * + * @param formalParameters The list of String representing the formal parameters. + * @param expression The <code>ValueExpression</code> representing the body. + */ + public LambdaExpression(List<String> formalParameters, ValueExpression expression) { + this.formalParameters = formalParameters; + this.expression = expression; + this.envirArgs = new HashMap<>(); + } + + /** + * Set the ELContext to use in evaluating the LambdaExpression. The ELContext must to be set prior to the invocation of + * the LambdaExpression, unless it is supplied with {@link LambdaExpression#invoke}. + * + * @param context The ELContext to use in evaluating the LambdaExpression. + */ + public void setELContext(ELContext context) { + this.context = context; + } + + /** + * Invoke the encapsulated Lambda expression. + * <p> + * The supplied arguments are matched, in the same order, to the formal parameters. If there are more arguments than the + * formal parameters, the extra arguments are ignored. If there are less arguments than the formal parameters, an + * <code>ELException</code> is thrown. + * </p> + * + * <p> + * The actual Lambda arguments are added to the ELContext and are available during the evaluation of the Lambda + * expression. They are removed after the evaluation. + * </p> + * + * @param elContext The ELContext used for the evaluation of the expression The ELContext set by {@link #setELContext} + * is ignored. + * @param args The arguments to invoke the Lambda expression. For calls with no arguments, an empty array must be + * provided. A Lambda argument can be <code>null</code>. + * @return The result of invoking the Lambda expression + * @throws ELException if not enough arguments are provided + * @throws NullPointerException is elContext is null + */ + public Object invoke(ELContext elContext, Object... args) throws ELException { + int i = 0; + Map<String, Object> lambdaArgs = new HashMap<>(); + + // First get arguments injected from the outter lambda, if any + lambdaArgs.putAll(envirArgs); + + for (String fParam : formalParameters) { + if (i >= args.length) { + throw new ELException("Expected Argument " + fParam + " missing in Lambda Expression"); + } + lambdaArgs.put(fParam, args[i++]); + } + + elContext.enterLambdaScope(lambdaArgs); + Object ret = expression.getValue(elContext); + + // If the result of evaluating the body is another LambdaExpression, + // whose body has not been evaluated yet. (A LambdaExpression is + // evaluated iff when its invoke method is called.) The current lambda + // arguments may be needed in that body when it is evaluated later, + // after the current lambda exits. To make these arguments available + // then, they are injected into it. + if (ret instanceof LambdaExpression) { + ((LambdaExpression) ret).envirArgs.putAll(lambdaArgs); + } + elContext.exitLambdaScope(); + + return ret; + } + + /** + * Invoke the encapsulated Lambda expression. + * <p> + * The supplied arguments are matched, in the same order, to the formal parameters. If there are more arguments than the + * formal parameters, the extra arguments are ignored. If there are less arguments than the formal parameters, an + * <code>ELException</code> is thrown. + * </p> + * + * <p> + * The actual Lambda arguments are added to the ELContext and are available during the evaluation of the Lambda + * expression. They are removed after the evaluation. + * </p> + * + * The ELContext set by {@link LambdaExpression#setELContext} is used in the evaluation of the lambda Expression. + * + * @param args The arguments to invoke the Lambda expression. For calls with no arguments, an empty array must be + * provided. A Lambda argument can be <code>null</code>. + * @return The result of invoking the Lambda expression + * @throws ELException if not enough arguments are provided + */ + public Object invoke(Object... args) { + return invoke(context, args); + } +}
diff --git a/api/src/main/java/javax/el/ListELResolver.java b/api/src/main/java/javax/el/ListELResolver.java new file mode 100644 index 0000000..4cd3a2e --- /dev/null +++ b/api/src/main/java/javax/el/ListELResolver.java
@@ -0,0 +1,333 @@ +/* + * Copyright (c) 1997-2018 Oracle and/or its affiliates and others. + * All rights reserved. + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.el; + +import java.beans.FeatureDescriptor; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +/** + * Defines property resolution behavior on instances of {@link java.util.List}. + * + * <p> + * This resolver handles base objects of type <code>java.util.List</code>. It accepts any object as a property and + * coerces that object into an integer index into the list. The resulting value is the value in the list at that index. + * </p> + * + * <p> + * This resolver can be constructed in read-only mode, which means that {@link #isReadOnly} will always return + * <code>true</code> and {@link #setValue} will always throw <code>PropertyNotWritableException</code>. + * </p> + * + * <p> + * <code>ELResolver</code>s are combined together using {@link CompositeELResolver}s, to define rich semantics for + * evaluating an expression. See the javadocs for {@link ELResolver} for details. + * </p> + * + * @see CompositeELResolver + * @see ELResolver + * @see java.util.List + * @since Jakarta Server Pages 2.1 + */ +public class ListELResolver extends ELResolver { + + static private Class<?> theUnmodifiableListClass = Collections.unmodifiableList(new ArrayList<>()).getClass(); + private boolean isReadOnly; + + /** + * Creates a new read/write <code>ListELResolver</code>. + */ + public ListELResolver() { + isReadOnly = false; + } + + /** + * Creates a new <code>ListELResolver</code> whose read-only status is determined by the given parameter. + * + * @param isReadOnly <code>true</code> if this resolver cannot modify lists; <code>false</code> otherwise. + */ + public ListELResolver(boolean isReadOnly) { + this.isReadOnly = isReadOnly; + } + + /** + * If the base object is a list, returns the most general acceptable type for a value in this list. + * + * <p> + * If the base is a <code>List</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object + * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after + * this method is called, the caller should ignore the return value. + * </p> + * + * <p> + * Assuming the base is a <code>List</code>, this method will always return <code>Object.class</code>. This is because + * <code>List</code>s accept any object as an element. + * </p> + * + * @param context The context of this evaluation. + * @param base The list to analyze. Only bases of type <code>List</code> are handled by this resolver. + * @param property The index of the element in the list to return the acceptable type for. Will be coerced into an + * integer, but otherwise ignored by this resolver. + * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then + * the most general acceptable type; otherwise undefined. + * @throws PropertyNotFoundException if the given index is out of bounds for this list. + * @throws NullPointerException if context is <code>null</code> + * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown + * exception must be included as the cause property of this exception, if available. + */ + @Override + public Class<?> getType(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException(); + } + + if (base != null && base instanceof List) { + context.setPropertyResolved(true); + List<?> list = (List<?>) base; + int index = toInteger(property); + if (index < 0 || index >= list.size()) { + throw new PropertyNotFoundException(); + } + + return Object.class; + } + + return null; + } + + /** + * If the base object is a list, returns the value at the given index. The index is specified by the + * <code>property</code> argument, and coerced into an integer. If the coercion could not be performed, an + * <code>IllegalArgumentException</code> is thrown. If the index is out of bounds, <code>null</code> is returned. + * + * <p> + * If the base is a <code>List</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object + * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after + * this method is called, the caller should ignore the return value. + * </p> + * + * @param context The context of this evaluation. + * @param base The list to be analyzed. Only bases of type <code>List</code> are handled by this resolver. + * @param property The index of the value to be returned. Will be coerced into an integer. + * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then + * the value at the given index or <code>null</code> if the index was out of bounds. Otherwise, undefined. + * @throws IllegalArgumentException if the property could not be coerced into an integer. + * @throws NullPointerException if context is <code>null</code>. + * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown + * exception must be included as the cause property of this exception, if available. + */ + @Override + public Object getValue(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException(); + } + + if (base != null && base instanceof List) { + context.setPropertyResolved(base, property); + List<?> list = (List<?>) base; + int index = toInteger(property); + if (index < 0 || index >= list.size()) { + return null; + } + + return list.get(index); + } + + return null; + } + + /** + * If the base object is a list, attempts to set the value at the given index with the given value. The index is + * specified by the <code>property</code> argument, and coerced into an integer. If the coercion could not be performed, + * an <code>IllegalArgumentException</code> is thrown. If the index is out of bounds, a + * <code>PropertyNotFoundException</code> is thrown. + * + * <p> + * If the base is a <code>List</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object + * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after + * this method is called, the caller can safely assume no value was set. + * </p> + * + * <p> + * If this resolver was constructed in read-only mode, this method will always throw + * <code>PropertyNotWritableException</code>. + * </p> + * + * <p> + * If a <code>List</code> was created using {@link java.util.Collections#unmodifiableList}, this method must throw + * <code>PropertyNotWritableException</code>. Unfortunately, there is no Collections API method to detect this. However, + * an implementation can create a prototype unmodifiable <code>List</code> and query its runtime type to see if it + * matches the runtime type of the base object as a workaround. + * </p> + * + * @param context The context of this evaluation. + * @param base The list to be modified. Only bases of type <code>List</code> are handled by this resolver. + * @param property The index of the value to be set. Will be coerced into an integer. + * @param val The value to be set at the given index. + * @throws ClassCastException if the class of the specified element prevents it from being added to this list. + * @throws NullPointerException if context is <code>null</code>, or if the value is <code>null</code> and this + * <code>List</code> does not support <code>null</code> elements. + * @throws IllegalArgumentException if the property could not be coerced into an integer, or if some aspect of the + * specified element prevents it from being added to this list. + * @throws PropertyNotWritableException if this resolver was constructed in read-only mode, or if the set operation is + * not supported by the underlying list. + * @throws PropertyNotFoundException if the given index is out of bounds for this list. + * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown + * exception must be included as the cause property of this exception, if available. + */ + @Override + public void setValue(ELContext context, Object base, Object property, Object val) { + if (context == null) { + throw new NullPointerException(); + } + + if (base != null && base instanceof List) { + context.setPropertyResolved(base, property); + // Safe cast + @SuppressWarnings("unchecked") + List<Object> list = (List<Object>) base; + int index = toInteger(property); + if (isReadOnly) { + throw new PropertyNotWritableException(); + } + + try { + list.set(index, val); + } catch (UnsupportedOperationException ex) { + throw new PropertyNotWritableException(); + } catch (IndexOutOfBoundsException ex) { + throw new PropertyNotFoundException(); + } catch (ClassCastException ex) { + throw ex; + } catch (NullPointerException ex) { + throw ex; + } catch (IllegalArgumentException ex) { + throw ex; + } + } + } + + /** + * If the base object is a list, returns whether a call to {@link #setValue} will always fail. + * + * <p> + * If the base is a <code>List</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object + * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after + * this method is called, the caller should ignore the return value. + * </p> + * + * <p> + * If this resolver was constructed in read-only mode, this method will always return <code>true</code>. + * </p> + * + * <p> + * If a <code>List</code> was created using {@link java.util.Collections#unmodifiableList}, this method must return + * <code>true</code>. Unfortunately, there is no Collections API method to detect this. However, an implementation can + * create a prototype unmodifiable <code>List</code> and query its runtime type to see if it matches the runtime type of + * the base object as a workaround. + * </p> + * + * @param context The context of this evaluation. + * @param base The list to analyze. Only bases of type <code>List</code> are handled by this resolver. + * @param property The index of the element in the list to return the acceptable type for. Will be coerced into an + * integer, but otherwise ignored by this resolver. + * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then + * <code>true</code> if calling the <code>setValue</code> method will always fail or <code>false</code> if it is + * possible that such a call may succeed; otherwise undefined. + * @throws PropertyNotFoundException if the given index is out of bounds for this list. + * @throws NullPointerException if context is <code>null</code> + * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown + * exception must be included as the cause property of this exception, if available. + */ + @Override + public boolean isReadOnly(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException(); + } + + if (base != null && base instanceof List) { + context.setPropertyResolved(true); + List<?> list = (List<?>) base; + int index = toInteger(property); + if (index < 0 || index >= list.size()) { + throw new PropertyNotFoundException(); + } + + return list.getClass() == theUnmodifiableListClass || isReadOnly; + } + + return false; + } + + /** + * Always returns <code>null</code>, since there is no reason to iterate through set set of all integers. + * + * <p> + * The {@link #getCommonPropertyType} method returns sufficient information about what properties this resolver accepts. + * </p> + * + * @param context The context of this evaluation. + * @param base The list. Only bases of type <code>List</code> are handled by this resolver. + * @return <code>null</code>. + */ + @Override + public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) { + return null; + } + + /** + * If the base object is a list, returns the most general type that this resolver accepts for the <code>property</code> + * argument. Otherwise, returns <code>null</code>. + * + * <p> + * Assuming the base is a <code>List</code>, this method will always return <code>Integer.class</code>. This is because + * <code>List</code>s accept integers as their index. + * </p> + * + * @param context The context of this evaluation. + * @param base The list to analyze. Only bases of type <code>List</code> are handled by this resolver. + * @return <code>null</code> if base is not a <code>List</code>; otherwise <code>Integer.class</code>. + */ + @Override + public Class<?> getCommonPropertyType(ELContext context, Object base) { + if (base != null && base instanceof List) { + return Integer.class; + } + + return null; + } + + private int toInteger(Object p) { + if (p instanceof Integer) { + return ((Integer) p).intValue(); + } + if (p instanceof Character) { + return ((Character) p).charValue(); + } + if (p instanceof Number) { + return ((Number) p).intValue(); + } + if (p instanceof String) { + return Integer.parseInt((String) p); + } + throw new IllegalArgumentException(); + } +}
diff --git a/api/src/main/java/javax/el/MapELResolver.java b/api/src/main/java/javax/el/MapELResolver.java new file mode 100644 index 0000000..cca84f2 --- /dev/null +++ b/api/src/main/java/javax/el/MapELResolver.java
@@ -0,0 +1,343 @@ +/* + * Copyright (c) 1997-2018 Oracle and/or its affiliates and others. + * All rights reserved. + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.el; + +import static java.lang.Boolean.TRUE; + +import java.beans.FeatureDescriptor; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Defines property resolution behavior on instances of {@link java.util.Map}. + * + * <p> + * This resolver handles base objects of type <code>java.util.Map</code>. It accepts any object as a property and uses + * that object as a key in the map. The resulting value is the value in the map that is associated with that key. + * </p> + * + * <p> + * This resolver can be constructed in read-only mode, which means that {@link #isReadOnly} will always return + * <code>true</code> and {@link #setValue} will always throw <code>PropertyNotWritableException</code>. + * </p> + * + * <p> + * <code>ELResolver</code>s are combined together using {@link CompositeELResolver}s, to define rich semantics for + * evaluating an expression. See the javadocs for {@link ELResolver} for details. + * </p> + * + * @see CompositeELResolver + * @see ELResolver + * @see java.util.Map + * @since Jakarta Server Pages 2.1 + */ +public class MapELResolver extends ELResolver { + + static private Class<?> theUnmodifiableMapClass = Collections.unmodifiableMap(new HashMap<>()).getClass(); + private boolean isReadOnly; + + /** + * Creates a new read/write <code>MapELResolver</code>. + */ + public MapELResolver() { + isReadOnly = false; + } + + /** + * Creates a new <code>MapELResolver</code> whose read-only status is determined by the given parameter. + * + * @param isReadOnly <code>true</code> if this resolver cannot modify maps; <code>false</code> otherwise. + */ + public MapELResolver(boolean isReadOnly) { + this.isReadOnly = isReadOnly; + } + + /** + * If the base object is a map, returns the most general acceptable type for a value in this map. + * + * <p> + * If the base is a <code>Map</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object + * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after + * this method is called, the caller should ignore the return value. + * </p> + * + * <p> + * Assuming the base is a <code>Map</code>, this method will always return <code>Object.class</code>. This is because + * <code>Map</code>s accept any object as the value for a given key. + * </p> + * + * @param context The context of this evaluation. + * @param base The map to analyze. Only bases of type <code>Map</code> are handled by this resolver. + * @param property The key to return the acceptable type for. Ignored by this resolver. + * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then + * the most general acceptable type; otherwise undefined. + * @throws NullPointerException if context is <code>null</code> + * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown + * exception must be included as the cause property of this exception, if available. + */ + @Override + public Class<?> getType(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException(); + } + + if (base != null && base instanceof Map) { + context.setPropertyResolved(true); + return Object.class; + } + + return null; + } + + /** + * If the base object is a map, returns the value associated with the given key, as specified by the + * <code>property</code> argument. If the key was not found, <code>null</code> is returned. + * + * <p> + * If the base is a <code>Map</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object + * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after + * this method is called, the caller should ignore the return value. + * </p> + * + * <p> + * Just as in {@link java.util.Map#get}, just because <code>null</code> is returned doesn't mean there is no mapping for + * the key; it's also possible that the <code>Map</code> explicitly maps the key to <code>null</code>. + * </p> + * + * @param context The context of this evaluation. + * @param base The map to be analyzed. Only bases of type <code>Map</code> are handled by this resolver. + * @param property The key whose associated value is to be returned. + * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then + * the value associated with the given key or <code>null</code> if the key was not found. Otherwise, undefined. + * @throws ClassCastException if the key is of an inappropriate type for this map (optionally thrown by the underlying + * <code>Map</code>). + * @throws NullPointerException if context is <code>null</code>, or if the key is null and this map does not permit null + * keys (the latter is optionally thrown by the underlying <code>Map</code>). + * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown + * exception must be included as the cause property of this exception, if available. + */ + @Override + public Object getValue(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException(); + } + + if (base != null && base instanceof Map) { + context.setPropertyResolved(base, property); + Map<?, ?> map = (Map<?, ?>) base; + return map.get(property); + } + + return null; + } + + /** + * If the base object is a map, attempts to set the value associated with the given key, as specified by the + * <code>property</code> argument. + * + * <p> + * If the base is a <code>Map</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object + * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after + * this method is called, the caller can safely assume no value was set. + * </p> + * + * <p> + * If this resolver was constructed in read-only mode, this method will always throw + * <code>PropertyNotWritableException</code>. + * </p> + * + * <p> + * If a <code>Map</code> was created using {@link java.util.Collections#unmodifiableMap}, this method must throw + * <code>PropertyNotWritableException</code>. Unfortunately, there is no Collections API method to detect this. However, + * an implementation can create a prototype unmodifiable <code>Map</code> and query its runtime type to see if it + * matches the runtime type of the base object as a workaround. + * </p> + * + * @param context The context of this evaluation. + * @param base The map to be modified. Only bases of type <code>Map</code> are handled by this resolver. + * @param property The key with which the specified value is to be associated. + * @param val The value to be associated with the specified key. + * @throws ClassCastException if the class of the specified key or value prevents it from being stored in this map. + * @throws NullPointerException if context is <code>null</code>, or if this map does not permit <code>null</code> keys + * or values, and the specified key or value is <code>null</code>. + * @throws IllegalArgumentException if some aspect of this key or value prevents it from being stored in this map. + * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown + * exception must be included as the cause property of this exception, if available. + * @throws PropertyNotWritableException if this resolver was constructed in read-only mode, or if the put operation is + * not supported by the underlying map. + */ + @Override + public void setValue(ELContext context, Object base, Object property, Object val) { + if (context == null) { + throw new NullPointerException(); + } + + if (base != null && base instanceof Map) { + context.setPropertyResolved(base, property); + + // The cast is safe + @SuppressWarnings("unchecked") + Map<Object, Object> map = (Map<Object, Object>) base; + if (isReadOnly || map.getClass() == theUnmodifiableMapClass) { + throw new PropertyNotWritableException(); + } + + try { + map.put(property, val); + } catch (UnsupportedOperationException ex) { + throw new PropertyNotWritableException(); + } + } + } + + /** + * If the base object is a map, returns whether a call to {@link #setValue} will always fail. + * + * <p> + * If the base is a <code>Map</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> object + * must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> after + * this method is called, the caller should ignore the return value. + * </p> + * + * <p> + * If this resolver was constructed in read-only mode, this method will always return <code>true</code>. + * </p> + * + * <p> + * If a <code>Map</code> was created using {@link java.util.Collections#unmodifiableMap}, this method must return + * <code>true</code>. Unfortunately, there is no Collections API method to detect this. However, an implementation can + * create a prototype unmodifiable <code>Map</code> and query its runtime type to see if it matches the runtime type of + * the base object as a workaround. + * </p> + * + * @param context The context of this evaluation. + * @param base The map to analyze. Only bases of type <code>Map</code> are handled by this resolver. + * @param property The key to return the read-only status for. Ignored by this resolver. + * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then + * <code>true</code> if calling the <code>setValue</code> method will always fail or <code>false</code> if it is + * possible that such a call may succeed; otherwise undefined. + * @throws NullPointerException if context is <code>null</code> + * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown + * exception must be included as the cause property of this exception, if available. + */ + @Override + public boolean isReadOnly(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException(); + } + + if (base != null && base instanceof Map) { + context.setPropertyResolved(true); + Map<?, ?> map = (Map<?, ?>) base; + return isReadOnly || map.getClass() == theUnmodifiableMapClass; + } + + return false; + } + + /** + * If the base object is a map, returns an <code>Iterator</code> containing the set of keys available in the + * <code>Map</code>. Otherwise, returns <code>null</code>. + * + * <p> + * The <code>Iterator</code> returned must contain zero or more instances of {@link java.beans.FeatureDescriptor}. Each + * info object contains information about a key in the Map, and is initialized as follows: + * <ul> + * <li>displayName - The return value of calling the <code>toString</code> method on this key, or <code>"null"</code> if + * the key is <code>null</code>.</li> + * <li>name - Same as displayName property.</li> + * <li>shortDescription - Empty string</li> + * <li>expert - <code>false</code></li> + * <li>hidden - <code>false</code></li> + * <li>preferred - <code>true</code></li> + * </ul> + * + * In addition, the following named attributes must be set in the returned <code>FeatureDescriptor</code>s: + * <ul> + * <li>{@link ELResolver#TYPE} - The return value of calling the <code>getClass()</code> method on this key, or + * <code>null</code> if the key is <code>null</code>.</li> + * <li>{@link ELResolver#RESOLVABLE_AT_DESIGN_TIME} - <code>true</code></li> + * </ul> + * + * + * @param context The context of this evaluation. + * @param base The map whose keys are to be iterated over. Only bases of type <code>Map</code> are handled by this + * resolver. + * @return An <code>Iterator</code> containing zero or more (possibly infinitely more) <code>FeatureDescriptor</code> + * objects, each representing a key in this map, or <code>null</code> if the base object is not a map. + */ + @Override + public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) { + if (base != null && base instanceof Map) { + Map<?, ?> map = (Map<?, ?>) base; + Iterator<?> iter = map.keySet().iterator(); + List<FeatureDescriptor> list = new ArrayList<>(); + + while (iter.hasNext()) { + Object key = iter.next(); + FeatureDescriptor descriptor = new FeatureDescriptor(); + String name = key == null ? null : key.toString(); + descriptor.setName(name); + descriptor.setDisplayName(name); + descriptor.setShortDescription(""); + descriptor.setExpert(false); + descriptor.setHidden(false); + descriptor.setPreferred(true); + + if (key != null) { + descriptor.setValue("type", key.getClass()); + } + + descriptor.setValue("resolvableAtDesignTime", TRUE); + list.add(descriptor); + } + + return list.iterator(); + } + + return null; + } + + /** + * If the base object is a map, returns the most general type that this resolver accepts for the <code>property</code> + * argument. Otherwise, returns <code>null</code>. + * + * <p> + * Assuming the base is a <code>Map</code>, this method will always return <code>Object.class</code>. This is because + * <code>Map</code>s accept any object as a key. + * </p> + * + * @param context The context of this evaluation. + * @param base The map to analyze. Only bases of type <code>Map</code> are handled by this resolver. + * @return <code>null</code> if base is not a <code>Map</code>; otherwise <code>Object.class</code>. + */ + @Override + public Class<?> getCommonPropertyType(ELContext context, Object base) { + if (base != null && base instanceof Map) { + return Object.class; + } + + return null; + } + +}
diff --git a/api/src/main/java/javax/el/MethodExpression.java b/api/src/main/java/javax/el/MethodExpression.java new file mode 100644 index 0000000..38b02e5 --- /dev/null +++ b/api/src/main/java/javax/el/MethodExpression.java
@@ -0,0 +1,123 @@ +/* + * Copyright (c) 1997-2018 Oracle and/or its affiliates and others. + * All rights reserved. + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.el; + +/** + * An <code>Expression</code> that refers to a method on an object. + * + * <p> + * The {@link javax.el.ExpressionFactory#createMethodExpression} method can be used to parse an expression string and + * return a concrete instance of <code>MethodExpression</code> that encapsulates the parsed expression. The + * {@link FunctionMapper} is used at parse time, not evaluation time, so one is not needed to evaluate an expression + * using this class. However, the {@link ELContext} is needed at evaluation time. + * </p> + * + * <p> + * The {@link #getMethodInfo} and {@link #invoke} methods will evaluate the expression each time they are called. The + * {@link ELResolver} in the <code>ELContext</code> is used to resolve the top-level variables and to determine the + * behavior of the <code>.</code> and <code>[]</code> operators. For any of the two methods, the + * {@link javax.el.ELResolver#getValue} method is used to resolve all properties up to but excluding the last one. This + * provides the <code>base</code> object on which the method appears. If the <code>base</code> object is null, a + * <code>PropertyNotFoundException</code> must be thrown. At the last resolution, the final <code>property</code> is + * then coerced to a <code>String</code>, which provides the name of the method to be found. A method matching the name + * and expected parameters provided at parse time is found and it is either queried or invoked (depending on the method + * called on this <code>MethodExpression</code>). + * </p> + * + * <p> + * See the notes about comparison, serialization and immutability in the {@link Expression} javadocs. + * + * @see ELResolver + * @see Expression + * @see ExpressionFactory + * @since Jakarta Server Pages 2.1 + */ +public abstract class MethodExpression extends Expression { + + private static final long serialVersionUID = -1151639017737837708L; + + // Evaluation + + /** + * Evaluates the expression relative to the provided context, and returns information about the actual referenced + * method. + * + * @param context The context of this evaluation + * @return an instance of <code>MethodInfo</code> containing information about the method the expression evaluated to. + * @throws NullPointerException if context is <code>null</code> + * @throws PropertyNotFoundException if one of the property resolutions failed because a specified variable or property + * does not exist or is not readable. + * @throws MethodNotFoundException if no suitable method can be found. + * @throws ELException if an exception was thrown while performing property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if available. + */ + public abstract MethodInfo getMethodInfo(ELContext context); + + /** + * If a String literal is specified as the expression, returns the String literal coerced to the expected return type of + * the method signature. An <code>ELException</code> is thrown if <code>expectedReturnType</code> is void or if the + * coercion of the String literal to the <code>expectedReturnType</code> yields an error (see Section "1.18 Type + * Conversion" of the Jakarta Expression Language specification). + * + * If not a String literal, evaluates the expression relative to the provided context, invokes the method that was found + * using the supplied parameters, and returns the result of the method invocation. + * + * Any parameters passed to this method is ignored if isLiteralText() or isParmetersProvided() is true. + * + * @param context The context of this evaluation. + * @param params The parameters to pass to the method, or <code>null</code> if no parameters. + * @return the result of the method invocation (<code>null</code> if the method has a <code>void</code> return type). + * @throws NullPointerException if context is <code>null</code> + * @throws PropertyNotFoundException if one of the property resolutions failed because a specified variable or property + * does not exist or is not readable. + * @throws MethodNotFoundException if no suitable method can be found. + * @throws ELException if a String literal is specified and expectedReturnType of the MethodExpression is void or if the + * coercion of the String literal to the expectedReturnType yields an error (see Section "1.18 Type Conversion"). + * @throws ELException if an exception was thrown while performing property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if available. If the exception thrown is an + * <code>InvocationTargetException</code>, extract its <code>cause</code> and pass it to the <code>ELException</code> + * constructor. + */ + public abstract Object invoke(ELContext context, Object[] params); + + /** + * Return whether this MethodExpression was created with parameters. + * + * <p> + * This method must return <code>true</code> if and only if parameters are specified in the EL, using the + * expr-a.expr-b(...) syntax. + * + * @return <code>true</code> if the MethodExpression was created with parameters, <code>false</code> otherwise. + * + * @since Jakarta Expression Language 2.2 + */ + public boolean isParametersProvided() { + return false; + } + + /** + * Use isParametersProvided instead. + * + * @return <code>true</code> if the MethodExpression was created with parameters, <code>false</code> otherwise. + */ + @Deprecated + public boolean isParmetersProvided() { + return isParametersProvided(); + } +}
diff --git a/api/src/main/java/javax/el/MethodInfo.java b/api/src/main/java/javax/el/MethodInfo.java new file mode 100644 index 0000000..b79f077 --- /dev/null +++ b/api/src/main/java/javax/el/MethodInfo.java
@@ -0,0 +1,72 @@ +/* + * Copyright (c) 1997-2018 Oracle and/or its affiliates and others. + * All rights reserved. + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.el; + +/** + * Holds information about a method that a {@link MethodExpression} evaluated to. + * + * @since Jakarta Server Pages 2.1 + */ +public class MethodInfo { + + private String name; + private Class<?> returnType; + private Class<?>[] paramTypes; + + /** + * Creates a new instance of <code>MethodInfo</code> with the given information. + * + * @param name The name of the method + * @param returnType The return type of the method + * @param paramTypes The types of each of the method's parameters + */ + public MethodInfo(String name, Class<?> returnType, Class<?>[] paramTypes) { + this.name = name; + this.returnType = returnType; + this.paramTypes = paramTypes; + } + + /** + * Returns the name of the method + * + * @return the name of the method + */ + public String getName() { + return name; + } + + /** + * Returns the return type of the method + * + * @return the return type of the method + */ + public Class<?> getReturnType() { + return returnType; + } + + /** + * Returns the parameter types of the method + * + * @return the parameter types of the method + */ + public Class<?>[] getParamTypes() { + return paramTypes; + } + +}
diff --git a/api/src/main/java/javax/el/MethodNotFoundException.java b/api/src/main/java/javax/el/MethodNotFoundException.java new file mode 100644 index 0000000..a306094 --- /dev/null +++ b/api/src/main/java/javax/el/MethodNotFoundException.java
@@ -0,0 +1,65 @@ +/* + * Copyright (c) 1997-2018 Oracle and/or its affiliates and others. + * All rights reserved. + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.el; + +/** + * Thrown when a method could not be found while evaluating a {@link MethodExpression}. + * + * @see MethodExpression + * @since Jakarta Server Pages 2.1 + */ +public class MethodNotFoundException extends ELException { + + private static final long serialVersionUID = 7727548537051164640L; + + /** + * Creates a <code>MethodNotFoundException</code> with no detail message. + */ + public MethodNotFoundException() { + super(); + } + + /** + * Creates a <code>MethodNotFoundException</code> with the provided detail message. + * + * @param message the detail message + */ + public MethodNotFoundException(String message) { + super(message); + } + + /** + * Creates a <code>MethodNotFoundException</code> with the given root cause. + * + * @param exception the originating cause of this exception + */ + public MethodNotFoundException(Throwable exception) { + super(exception); + } + + /** + * Creates a <code>MethodNotFoundException</code> with the given detail message and root cause. + * + * @param pMessage the detail message + * @param pRootCause the originating cause of this exception + */ + public MethodNotFoundException(String pMessage, Throwable pRootCause) { + super(pMessage, pRootCause); + } +}
diff --git a/api/src/main/java/javax/el/PrivateMessages.properties b/api/src/main/java/javax/el/PrivateMessages.properties new file mode 100644 index 0000000..e628977 --- /dev/null +++ b/api/src/main/java/javax/el/PrivateMessages.properties
@@ -0,0 +1,27 @@ +# +# Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. +# Copyright 2004 The Apache Software Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This properties file is essentially "package private" but because +# there is no way to attach an access specifier to a properties file we +# are including this comment to serve as such. +setPropertyFailed=Can''t set property ''{0}'' on class ''{1}'' to value ''{2}''. +propertyNotFound=The class ''{0}'' does not have the property ''{1}''. +propertyNotReadable=The class ''{0}'' does not have a readable property ''{1}''. +resolverNotWritable=The ELResolver for the class ''{0}'' is not writable. +propertyNotWritable=The class ''{0}'' does not have a writable property ''{1}''. +staticFieldReadError=Either ''{1}'' is not a public static field of the class ''{0}'' or field is inaccessible +staticFieldWriteError=Cannot write to the field ''{1}}'' of the class ''{0}''
diff --git a/api/src/main/java/javax/el/PropertyNotFoundException.java b/api/src/main/java/javax/el/PropertyNotFoundException.java new file mode 100644 index 0000000..8a829e1 --- /dev/null +++ b/api/src/main/java/javax/el/PropertyNotFoundException.java
@@ -0,0 +1,72 @@ +/* + * Copyright (c) 1997-2018 Oracle and/or its affiliates and others. + * All rights reserved. + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.el; + +/** + * Thrown when a property could not be found while evaluating a {@link ValueExpression} or {@link MethodExpression}. + * + * <p> + * For example, this could be triggered by an index out of bounds while setting an array value, or by an unreadable + * property while getting the value of a JavaBeans property. + * </p> + * + * @since Jakarta Server Pages 2.1 + */ +public class PropertyNotFoundException extends ELException { + + private static final long serialVersionUID = 7876728153282609955L; + + // ------------------------------------- + /** + * Creates a <code>PropertyNotFoundException</code> with no detail message. + */ + public PropertyNotFoundException() { + super(); + } + + // ------------------------------------- + /** + * Creates a <code>PropertyNotFoundException</code> with the provided detail message. + * + * @param message the detail message + */ + public PropertyNotFoundException(String message) { + super(message); + } + + /** + * Creates a <code>PropertyNotFoundException</code> with the given root cause. + * + * @param exception the originating cause of this exception + */ + public PropertyNotFoundException(Throwable exception) { + super(exception); + } + + /** + * Creates a <code>PropertyNotFoundException</code> with the given detail message and root cause. + * + * @param pMessage the detail message + * @param pRootCause the originating cause of this exception + */ + public PropertyNotFoundException(String pMessage, Throwable pRootCause) { + super(pMessage, pRootCause); + } + +}
diff --git a/api/src/main/java/javax/el/PropertyNotWritableException.java b/api/src/main/java/javax/el/PropertyNotWritableException.java new file mode 100644 index 0000000..e814168 --- /dev/null +++ b/api/src/main/java/javax/el/PropertyNotWritableException.java
@@ -0,0 +1,69 @@ +/* + * Copyright (c) 1997-2018 Oracle and/or its affiliates and others. + * All rights reserved. + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.el; + +/** + * Thrown when a property could not be written to while setting the value on a {@link ValueExpression}. + * + * <p> + * For example, this could be triggered by trying to set a map value on an unmodifiable map. + * </p> + * + * @since Jakarta Server Pages 2.1 + */ +public class PropertyNotWritableException extends ELException { + + private static final long serialVersionUID = 4511862414551151572L; + + /** + * Creates a <code>PropertyNotWritableException</code> with no detail message. + */ + public PropertyNotWritableException() { + super(); + } + + /** + * Creates a <code>PropertyNotWritableException</code> with the provided detail message. + * + * @param pMessage the detail message + */ + public PropertyNotWritableException(String pMessage) { + super(pMessage); + } + + /** + * Creates a <code>PropertyNotWritableException</code> with the given root cause. + * + * @param exception the originating cause of this exception + */ + public PropertyNotWritableException(Throwable exception) { + super(exception); + } + + /** + * Creates a <code>PropertyNotWritableException</code> with the given detail message and root cause. + * + * @param pMessage the detail message + * @param pRootCause the originating cause of this exception + */ + public PropertyNotWritableException(String pMessage, Throwable pRootCause) { + super(pMessage, pRootCause); + } + +}
diff --git a/api/src/main/java/javax/el/ResourceBundleELResolver.java b/api/src/main/java/javax/el/ResourceBundleELResolver.java new file mode 100644 index 0000000..cd5e02f --- /dev/null +++ b/api/src/main/java/javax/el/ResourceBundleELResolver.java
@@ -0,0 +1,246 @@ +/* + * Copyright (c) 1997-2018 Oracle and/or its affiliates and others. + * All rights reserved. + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.el; + +import static java.lang.Boolean.TRUE; + +import java.beans.FeatureDescriptor; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.List; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * Defines property resolution behavior on instances of {@link java.util.ResourceBundle}. + * + * <p> + * This resolver handles base objects of type <code>java.util.ResourceBundle</code>. It accepts any object as a property + * and coerces it to a <code>java.lang.String</code> for invoking + * {@link java.util.ResourceBundle#getObject(java.lang.String)}. + * + * <p> + * This resolver is read only and will throw a {@link PropertyNotWritableException} if <code>setValue</code> is called. + * + * <p> + * <code>ELResolver</code>s are combined together using {@link CompositeELResolver}s, to define rich semantics for + * evaluating an expression. See the javadocs for {@link ELResolver} for details. + * + * @see CompositeELResolver + * @see ELResolver + * @see java.util.ResourceBundle + * + * @since Jakarta Server Pages 2.1 + */ +public class ResourceBundleELResolver extends ELResolver { + + /** + * If the base object is an instance of <code>ResourceBundle</code>, the provided property will first be coerced to a + * <code>String</code>. The <code>Object</code> returned by <code>getObject</code> on the base + * <code>ResourceBundle</code> will be returned. + * + * <p> + * If the base is <code>ResourceBundle</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> + * object must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> + * after this method is called, the caller should ignore the return value. + * + * @param context The context of this evaluation. + * @param base The ResourceBundle to analyze. + * @param property The name of the property to analyze. Will be coerced to a <code>String</code>. + * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then + * <code>null</code> if property is <code>null</code>; otherwise the <code>Object</code> for the given key (property + * coerced to <code>String</code>) from the <code>ResourceBundle</code>. If no object for the given key can be found, + * then the <code>String</code> "???" + key + "???". + * @throws NullPointerException if context is <code>null</code> + * @throws ELException if an exception was thrown while performing the property or variable resolution. The thrown + * exception must be included as the cause property of this exception, if available. + */ + @Override + public Object getValue(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException(); + } + + if (base instanceof ResourceBundle) { + context.setPropertyResolved(true); + if (property != null) { + try { + return ((ResourceBundle) base).getObject(property.toString()); + } catch (MissingResourceException e) { + return "???" + property + "???"; + } + } + } + + return null; + } + + /** + * If the base object is an instance of <code>ResourceBundle</code>, return <code>null</code>, since the resolver is + * read only. + * + * <p> + * If the base is <code>ResourceBundle</code>, the <code>propertyResolved</code> property of the <code>ELContext</code> + * object must be set to <code>true</code> by this resolver, before returning. If this property is not <code>true</code> + * after this method is called, the caller should ignore the return value. + * </p> + * + * @param context The context of this evaluation. + * @param base The ResourceBundle to analyze. + * @param property The name of the property to analyze. + * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then + * <code>null</code>; otherwise undefined. + * @throws NullPointerException if context is <code>null</code> + */ + @Override + public Class<?> getType(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException(); + } + + if (base instanceof ResourceBundle) { + context.setPropertyResolved(true); + } + + return null; + } + + /** + * If the base object is a ResourceBundle, throw a {@link PropertyNotWritableException}. + * + * @param context The context of this evaluation. + * @param base The ResourceBundle to be modified. Only bases that are of type ResourceBundle are handled. + * @param property The String property to use. + * @param value The value to be set. + * @throws NullPointerException if context is <code>null</code>. + * @throws PropertyNotWritableException Always thrown if base is an instance of ReasourceBundle. + */ + @Override + public void setValue(ELContext context, Object base, Object property, Object value) { + if (context == null) { + throw new NullPointerException(); + } + + if (base instanceof ResourceBundle) { + context.setPropertyResolved(true); + throw new PropertyNotWritableException("ResourceBundles are immutable"); + } + } + + /** + * If the base object is not null and an <code>instanceof</code> {@link ResourceBundle}, return <code>true</code>. + * + * @param context The context of this evaluation. + * @param base The ResourceBundle to be modified. Only bases that are of type ResourceBundle are handled. + * @param property The String property to use. + * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then + * <code>true</code>; otherwise undefined. + * @throws NullPointerException if context is <code>null</code> + */ + @Override + public boolean isReadOnly(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException(); + } + + if (base instanceof ResourceBundle) { + context.setPropertyResolved(true); + return true; + } + + return false; + } + + /** + * If the base object is a ResourceBundle, returns an <code>Iterator</code> containing the set of keys available in the + * <code>ResourceBundle</code>. Otherwise, returns <code>null</code>. + * + * <p> + * The <code>Iterator</code> returned must contain zero or more instances of {@link java.beans.FeatureDescriptor}. Each + * info object contains information about a key in the ResourceBundle, and is initialized as follows: + * <ul> + * <li>displayName - The <code>String</code> key + * <li>name - Same as displayName property.</li> + * <li>shortDescription - Empty string</li> + * <li>expert - <code>false</code></li> + * <li>hidden - <code>false</code></li> + * <li>preferred - <code>true</code></li> + * </ul> + * + * In addition, the following named attributes must be set in the returned <code>FeatureDescriptor</code>s: + * <ul> + * <li>{@link ELResolver#TYPE} - <code>String.class</code></li> + * <li>{@link ELResolver#RESOLVABLE_AT_DESIGN_TIME} - <code>true</code></li> + * </ul> + * + * + * @param context The context of this evaluation. + * @param base The bundle whose keys are to be iterated over. Only bases of type <code>ResourceBundle</code> are handled + * by this resolver. + * @return An <code>Iterator</code> containing zero or more (possibly infinitely more) <code>FeatureDescriptor</code> + * objects, each representing a key in this bundle, or <code>null</code> if the base object is not a ResourceBundle. + */ + @Override + public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) { + if (base instanceof ResourceBundle) { + ResourceBundle bundle = (ResourceBundle) base; + List<FeatureDescriptor> features = new ArrayList<>(); + String key = null; + FeatureDescriptor desc = null; + + for (Enumeration<String> e = bundle.getKeys(); e.hasMoreElements();) { + key = e.nextElement(); + desc = new FeatureDescriptor(); + desc.setDisplayName(key); + desc.setExpert(false); + desc.setHidden(false); + desc.setName(key); + desc.setPreferred(true); + desc.setValue(TYPE, String.class); + desc.setValue(RESOLVABLE_AT_DESIGN_TIME, TRUE); + features.add(desc); + } + + return features.iterator(); + } + + return null; + } + + /** + * If the base object is a ResourceBundle, returns the most general type that this resolver accepts for the + * <code>property</code> argument. Otherwise, returns <code>null</code>. + * + * <p> + * Assuming the base is a <code>ResourceBundle</code>, this method will always return <code>String.class</code>. + * + * @param context The context of this evaluation. + * @param base The bundle to analyze. Only bases of type <code>ResourceBundle</code> are handled by this resolver. + * @return <code>null</code> if base is not a <code>ResourceBundle</code>; otherwise <code>String.class</code>. + */ + @Override + public Class<?> getCommonPropertyType(ELContext context, Object base) { + if (base instanceof ResourceBundle) { + return String.class; + } + + return null; + } +}
diff --git a/api/src/main/java/javax/el/StandardELContext.java b/api/src/main/java/javax/el/StandardELContext.java new file mode 100644 index 0000000..1c17be7 --- /dev/null +++ b/api/src/main/java/javax/el/StandardELContext.java
@@ -0,0 +1,292 @@ +/* + * Copyright (c) 2012, 2018 Oracle and/or its affiliates and others. + * All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.el; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +/** + * A standard ELContext suitable for use in a stand alone environment. This class provides a default implementation of + * an ELResolver that contains a number of useful ELResolvers. It also provides local repositories for the + * FunctionMapper, VariableMapper, and BeanNameResolver. + * + * @since Jakarta Expression Language 3.0 + */ +public class StandardELContext extends ELContext { + + /* + * The ELResolver for this ELContext. + */ + private ELResolver elResolver; + + /* + * The list of the custom ELResolvers added to the ELResolvers. An ELResolver is added to the list when addELResolver is + * called. + */ + private CompositeELResolver customResolvers; + + /* + * The ELResolver implementing the query operators. + */ + private ELResolver streamELResolver; + + /* + * The FunctionMapper for this ELContext. + */ + private FunctionMapper functionMapper; + + /* + * The pre-configured init function map; + */ + private Map<String, Method> initFunctionMap; + + /* + * The VariableMapper for this ELContext. + */ + private VariableMapper variableMapper; + + /* + * If non-null, indicates the presence of a delegate ELContext. When a Standard is constructed from another ELContext, + * there is no easy way to get its private context map, therefore delegation is needed. + */ + private ELContext delegate; + + /** + * A bean repository local to this context + */ + private Map<String, Object> beans = new HashMap<>(); + + /** + * Construct a default ELContext for a stand-alone environment. + * + * @param factory The ExpressionFactory + */ + public StandardELContext(ExpressionFactory factory) { + streamELResolver = factory.getStreamELResolver(); + initFunctionMap = factory.getInitFunctionMap(); + } + + /** + * Construct a StandardELContext from another ELContext. + * + * @param context The ELContext that acts as a delegate in most cases + */ + public StandardELContext(ELContext context) { + delegate = context; + + // Copy all attributes except map and resolved + CompositeELResolver compositeELResolver = new CompositeELResolver(); + compositeELResolver.add(new BeanNameELResolver(new LocalBeanNameResolver())); + customResolvers = new CompositeELResolver(); + + compositeELResolver.add(customResolvers); + compositeELResolver.add(context.getELResolver()); + elResolver = compositeELResolver; + + functionMapper = context.getFunctionMapper(); + variableMapper = context.getVariableMapper(); + setLocale(context.getLocale()); + } + + @Override + public void putContext(Class key, Object contextObject) { + if (delegate != null) { + delegate.putContext(key, contextObject); + } else { + super.putContext(key, contextObject); + } + } + + @Override + public Object getContext(Class key) { + if (delegate == null) { + return super.getContext(key); + } + + return delegate.getContext(key); + } + + /** + * Construct (if needed) and return a default ELResolver. + * + * <p> + * Retrieves the <code>ELResolver</code> associated with this context. This is a <code>CompositeELResover</code> + * consists of an ordered list of <code>ELResolver</code>s. + * + * <ol> + * <li>A {@link BeanNameELResolver} for beans defined locally</li> + * <li>Any custom <code>ELResolver</code>s</li> + * <li>An <code>ELResolver</code> supporting the collection operations</li> + * <li>A {@link StaticFieldELResolver} for resolving static fields</li> + * <li>A {@link MapELResolver} for resolving Map properties</li> + * <li>A {@link ResourceBundleELResolver} for resolving ResourceBundle properties</li> + * <li>A {@link ListELResolver} for resolving List properties</li> + * <li>An {@link ArrayELResolver} for resolving array properties</li> + * <li>A {@link BeanELResolver} for resolving bean properties</li> + * </ol> + * + * @return The ELResolver for this context. + */ + @Override + public ELResolver getELResolver() { + if (elResolver == null) { + CompositeELResolver resolver = new CompositeELResolver(); + customResolvers = new CompositeELResolver(); + resolver.add(customResolvers); + resolver.add(new BeanNameELResolver(new LocalBeanNameResolver())); + if (streamELResolver != null) { + resolver.add(streamELResolver); + } + resolver.add(new StaticFieldELResolver()); + resolver.add(new MapELResolver()); + resolver.add(new ResourceBundleELResolver()); + resolver.add(new ListELResolver()); + resolver.add(new ArrayELResolver()); + resolver.add(new BeanELResolver()); + elResolver = resolver; + } + + return elResolver; + } + + /** + * Add a custom ELResolver to the context. The list of the custom ELResolvers will be accessed in the order they are + * added. A custom ELResolver added to the context cannot be removed. + * + * @param cELResolver The new ELResolver to be added to the context + */ + public void addELResolver(ELResolver cELResolver) { + getELResolver(); // make sure elResolver is constructed + customResolvers.add(cELResolver); + } + + /** + * Get the local bean repository + * + * @return the bean repository + */ + Map<String, Object> getBeans() { + return beans; + } + + /** + * Construct (if needed) and return a default FunctionMapper. + * + * @return The default FunctionMapper + */ + @Override + public FunctionMapper getFunctionMapper() { + if (functionMapper == null) { + functionMapper = new DefaultFunctionMapper(initFunctionMap); + } + + return functionMapper; + } + + /** + * Construct (if needed) and return a default VariableMapper() { + * + * @return The default Variable + */ + @Override + public VariableMapper getVariableMapper() { + if (variableMapper == null) { + variableMapper = new DefaultVariableMapper(); + } + + return variableMapper; + } + + private static class DefaultFunctionMapper extends FunctionMapper { + + private Map<String, Method> functions; + + DefaultFunctionMapper(Map<String, Method> initMap) { + functions = (initMap == null) ? new HashMap<String, Method>() : new HashMap<>(initMap); + } + + @Override + public Method resolveFunction(String prefix, String localName) { + return functions.get(prefix + ":" + localName); + } + + @Override + public void mapFunction(String prefix, String localName, Method meth) { + functions.put(prefix + ":" + localName, meth); + } + } + + private static class DefaultVariableMapper extends VariableMapper { + + private Map<String, ValueExpression> variables; + + @Override + public ValueExpression resolveVariable(String variable) { + if (variables == null) { + return null; + } + + return variables.get(variable); + } + + @Override + public ValueExpression setVariable(String variable, ValueExpression expression) { + if (variables == null) { + variables = new HashMap<>(); + } + + ValueExpression prev = null; + if (expression == null) { + prev = variables.remove(variable); + } else { + prev = variables.put(variable, expression); + } + + return prev; + } + } + + private class LocalBeanNameResolver extends BeanNameResolver { + + @Override + public boolean isNameResolved(String beanName) { + return beans.containsKey(beanName); + } + + @Override + public Object getBean(String beanName) { + return beans.get(beanName); + } + + @Override + public void setBeanValue(String beanName, Object value) { + beans.put(beanName, value); + } + + @Override + public boolean isReadOnly(String beanName) { + return false; + } + + @Override + public boolean canCreateBean(String beanName) { + return true; + } + } +}
diff --git a/api/src/main/java/javax/el/StaticFieldELResolver.java b/api/src/main/java/javax/el/StaticFieldELResolver.java new file mode 100644 index 0000000..a945e0d --- /dev/null +++ b/api/src/main/java/javax/el/StaticFieldELResolver.java
@@ -0,0 +1,288 @@ +/* + * Copyright (c) 2012, 2018 Oracle and/or its affiliates and others. + * All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.el; + +import static java.lang.reflect.Modifier.isPublic; +import static java.lang.reflect.Modifier.isStatic; +import static javax.el.ELUtil.getExceptionMessageString; + +import java.beans.FeatureDescriptor; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Iterator; + +/** + * An {@link ELResolver} for resolving static fields, enum constants and static methods. Also handles constructor calls + * as a special case. + * + * <p> + * The resolver handles base objects of the type {@link ELClass}, which is usually generated by a Jakarta Expression + * Language implementation. + * + * @see ELClass + * @since Jakarta Expression Language 3.0 + */ +public class StaticFieldELResolver extends ELResolver { + + /** + * <p> + * Returns the value of a static field. + * </p> + * <p> + * If the base object is an instance of <code>ELClass</code> and the property is String, the + * <code>propertyResolved</code> property of the <code>ELContext</code> object must be set to <code>true</code> by this + * resolver, before returning. If this property is not <code>true</code> after this method is called, the caller should + * ignore the return value. + * </p> + * + * If the property is a public static field of class specified in <code>ELClass</code>, return the value of the static + * field. An Enum constant is a public static field of an Enum object, and is a special case of this. + * + * @param context The context of this evaluation. + * @param base An <code>ELClass</code>. + * @param property A static field name. + * + * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then + * the static field value. + * + * @throws NullPointerException if context is <code>null</code>. + * @throws PropertyNotFoundException if the specified class does not exist, or if the field is not a public static filed + * of the class, or if the field is inaccessible. + */ + @Override + public Object getValue(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException(); + } + + if (base instanceof ELClass && property instanceof String) { + Class<?> klass = ((ELClass) base).getKlass(); + String fieldName = (String) property; + try { + context.setPropertyResolved(base, property); + Field field = klass.getField(fieldName); + + int mod = field.getModifiers(); + if (Modifier.isPublic(mod) && Modifier.isStatic(mod)) { + return field.get(null); + } + } catch (NoSuchFieldException ex) { + } catch (IllegalAccessException ex) { + } + + throw new PropertyNotFoundException(ELUtil.getExceptionMessageString(context, "staticFieldReadError", new Object[] { klass.getName(), fieldName })); + } + + return null; + } + + /** + * <p> + * Attempts to write to a static field. + * </p> + * <p> + * If the base object is an instance of <code>ELClass</code>and the property is String, a + * <code>PropertyNotWritableException</code> will always be thrown, because writing to a static field is not allowed. + * + * @param context The context of this evaluation. + * @param base An <code>ELClass</code> + * @param property The name of the field + * @param value The value to set the field of the class to. + * @throws NullPointerException if context is <code>null</code> + * @throws PropertyNotWritableException if base object instance of <code>ELClass</code>and <code>property</code> + * instance of String + */ + @Override + public void setValue(ELContext context, Object base, Object property, Object value) { + if (context == null) { + throw new NullPointerException(); + } + + if (base instanceof ELClass && property instanceof String) { + Class<?> klass = ((ELClass) base).getKlass(); + String fieldName = (String) property; + throw new PropertyNotWritableException( + getExceptionMessageString(context, "staticFieldWriteError", new Object[] { klass.getName(), fieldName })); + } + } + + /** + * Invokes a public static method or the constructor for a class. + * + * <p> + * If the base object is an instance of <code>ELClass</code> and the method is a String, the + * <code>propertyResolved</code> property of the <code>ELContext</code> object must be set to <code>true</code> by the + * resolver, before returning. If this property is not <code>true</code> after this method is called, the caller should + * ignore the return value. + * + * <p> + * Invoke the public static method specified by <code>method</code>. + * + * <p> + * The process involved in the method selection is the same as that used in {@link BeanELResolver}. + * + * <p> + * As a special case, if the name of the method is "<init>", the constructor for the class will be invoked. + * + * @param base An <code>ELClass</code> + * @param methodName When coerced to a <code>String</code>, the simple name of the method. + * @param paramTypes An array of Class objects identifying the method's formal parameter types, in declared order. Use + * an empty array if the method has no parameters. Can be <code>null</code>, in which case the method's formal parameter + * types are assumed to be unknown. + * @param params The parameters to pass to the method, or <code>null</code> if no parameters. + * @return The result of the method invocation (<code>null</code> if the method has a <code>void</code> return type). + * @throws MethodNotFoundException if no suitable method can be found. + * @throws ELException if an exception was thrown while performing (base, method) resolution. The thrown exception must + * be included as the cause property of this exception, if available. If the exception thrown is an + * <code>InvocationTargetException</code>, extract its <code>cause</code> and pass it to the <code>ELException</code> + * constructor. + */ + @Override + public Object invoke(ELContext context, Object base, Object methodName, Class<?>[] paramTypes, Object[] params) { + if (context == null) { + throw new NullPointerException(); + } + + if (!(base instanceof ELClass && methodName instanceof String)) { + return null; + } + + Class<?> klass = ((ELClass) base).getKlass(); + String name = (String) methodName; + + Object ret; + if ("<init>".equals(name)) { + Constructor<?> constructor = ELUtil.findConstructor(klass, paramTypes, params); + ret = ELUtil.invokeConstructor(context, constructor, params); + } else { + Method method = ELUtil.findMethod(klass, name, paramTypes, params, true); + ret = ELUtil.invokeMethod(context, method, null, params); + } + context.setPropertyResolved(base, methodName); + + return ret; + } + + /** + * Returns the type of a static field. + * + * <p> + * If the base object is an instance of <code>ELClass</code>and the property is a String, the + * <code>propertyResolved</code> property of the <code>ELContext</code> object must be set to <code>true</code> by the + * resolver, before returning. If this property is not <code>true</code> after this method is called, the caller can + * safely assume no value has been set. + * + * <p> + * If the property string is a public static field of class specified in ELClass, return the type of the static field. + * + * @param context The context of this evaluation. + * @param base An <code>ELClass</code>. + * @param property The name of the field. + * @return If the <code>propertyResolved</code> property of <code>ELContext</code> was set to <code>true</code>, then + * the type of the type of the field. + * @throws NullPointerException if context is <code>null</code>. + * @throws PropertyNotFoundException if field is not a public static filed of the class, or if the field is + * inaccessible. + */ + @Override + public Class<?> getType(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException(); + } + + if (base instanceof ELClass && property instanceof String) { + Class<?> klass = ((ELClass) base).getKlass(); + String fieldName = (String) property; + try { + context.setPropertyResolved(true); + Field field = klass.getField(fieldName); + + int mod = field.getModifiers(); + if (isPublic(mod) && isStatic(mod)) { + return field.getType(); + } + } catch (NoSuchFieldException ex) { + } + throw new PropertyNotFoundException(getExceptionMessageString(context, "staticFieldReadError", new Object[] { klass.getName(), fieldName })); + } + + return null; + } + + /** + * <p> + * Inquires whether the static field is writable. + * </p> + * <p> + * If the base object is an instance of <code>ELClass</code>and the property is a String, the + * <code>propertyResolved</code> property of the <code>ELContext</code> object must be set to <code>true</code> by the + * resolver, before returning. If this property is not <code>true</code> after this method is called, the caller can + * safely assume no value has been set. + * </p> + * + * <p> + * Always returns a <code>true</code> because writing to a static field is not allowed. + * </p> + * + * @param context The context of this evaluation. + * @param base An <code>ELClass</code>. + * @param property The name of the bean. + * @return <code>true</code> + * @throws NullPointerException if context is <code>null</code>. + */ + @Override + public boolean isReadOnly(ELContext context, Object base, Object property) { + if (context == null) { + throw new NullPointerException(); + } + + if (base instanceof ELClass && property instanceof String) { + ((ELClass) base).getKlass(); + context.setPropertyResolved(true); + } + + return true; + } + + /** + * Returns the properties that can be resolved. Always returns <code>null</code>, since there is no reason to iterate + * through a list of one element: field name. + * + * @param context The context of this evaluation. + * @param base An <code>ELClass</code>. + * @return <code>null</code>. + */ + @Override + public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) { + return null; + } + + /** + * Returns the type of the property. Always returns <code>String.class</code>, since a field name is a String. + * + * @param context The context of this evaluation. + * @param base An <code>ELClass</code>. + * @return <code>String.class</code>. + */ + @Override + public Class<?> getCommonPropertyType(ELContext context, Object base) { + return String.class; + } +}
diff --git a/api/src/main/java/javax/el/TypeConverter.java b/api/src/main/java/javax/el/TypeConverter.java new file mode 100644 index 0000000..e9b3dcd --- /dev/null +++ b/api/src/main/java/javax/el/TypeConverter.java
@@ -0,0 +1,91 @@ +/* + * Copyright (c) 2012, 2018 Oracle and/or its affiliates and others. + * All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.el; + +import java.beans.FeatureDescriptor; +import java.util.Iterator; + +/** + * A convenient class for writing an ELResolver to do custom type conversions. + * + * <p> + * For example, to convert a String to an instance of MyDate, one can write + * + * <pre> + * <code> + * ELProcessor elp = new ELProcessor(); + * elp.getELManager().addELResolver(new TypeConverter() { + * Object convertToType(ELContext context, Object obj, Class<?> type) { + * if ((obj instanceof String) && type == MyDate.class) { + * context.setPropertyResolved(obj, type); + * return (obj == null)? null: new MyDate(obj.toString()); + * } + * return null; + * } + * }; + * </code> + * </pre> + * + * @since Jakarta Expression Language 3.0 + */ +public abstract class TypeConverter extends ELResolver { + + @Override + public Object getValue(ELContext context, Object base, Object property) { + return null; + } + + @Override + public Class<?> getType(ELContext context, Object base, Object property) { + return null; + } + + @Override + public void setValue(ELContext context, Object base, Object property, Object value) { + } + + @Override + public boolean isReadOnly(ELContext context, Object base, Object property) { + return false; + } + + @Override + public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) { + return null; + } + + @Override + public Class<?> getCommonPropertyType(ELContext context, Object base) { + return null; + } + + /** + * Converts an object to a specific type. + * + * <p> + * An <code>ELException</code> is thrown if an error occurs during the conversion. + * </p> + * + * @param context The context of this evaluation. + * @param obj The object to convert. + * @param targetType The target type for the conversion. + * @throws ELException thrown if errors occur. + */ + @Override + abstract public Object convertToType(ELContext context, Object obj, Class<?> targetType); +}
diff --git a/api/src/main/java/javax/el/ValueExpression.java b/api/src/main/java/javax/el/ValueExpression.java new file mode 100644 index 0000000..7f9cfdc --- /dev/null +++ b/api/src/main/java/javax/el/ValueExpression.java
@@ -0,0 +1,162 @@ +/* + * Copyright (c) 1997-2018 Oracle and/or its affiliates and others. + * All rights reserved. + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.el; + +/** + * An <code>Expression</code> that can get or set a value. + * + * <p> + * In previous incarnations of this API, expressions could only be read. <code>ValueExpression</code> objects can now be + * used both to retrieve a value and to set a value. Expressions that can have a value set on them are referred to as + * l-value expressions. Those that cannot are referred to as r-value expressions. Not all r-value expressions can be + * used as l-value expressions (e.g. <code>"${1+1}"</code> or <code>"${firstName} ${lastName}"</code>). See the EL + * Specification for details. Expressions that cannot be used as l-values must always return <code>true</code> from + * <code>isReadOnly()</code>. + * </p> + * + * <p> + * The <code>{@link ExpressionFactory#createValueExpression}</code> method can be used to parse an expression string and + * return a concrete instance of <code>ValueExpression</code> that encapsulates the parsed expression. The + * {@link FunctionMapper} is used at parse time, not evaluation time, so one is not needed to evaluate an expression + * using this class. However, the {@link ELContext} is needed at evaluation time. + * </p> + * + * <p> + * The {@link #getValue}, {@link #setValue}, {@link #isReadOnly}, {@link #getType} and {@link #getValueReference} + * methods will evaluate the expression each time they are called. The {@link ELResolver} in the <code>ELContext</code> + * is used to resolve the top-level variables and to determine the behavior of the <code>.</code> and <code>[]</code> + * operators. For any of the five methods, the {@link ELResolver#getValue} method is used to resolve all properties up + * to but excluding the last one. This provides the <code>base</code> object. For all methods other than the + * {@link #getValueReference} method, at the last resolution, the <code>ValueExpression</code> will call the + * corresponding {@link ELResolver#getValue}, {@link ELResolver#setValue}, {@link ELResolver#isReadOnly} or + * {@link ELResolver#getType} method, depending on which was called on the <code>ValueExpression</code>. For the + * {@link #getValueReference} method, the (base, property) is not resolved by the ELResolver, but an instance of + * {@link ValueReference} is created to encapsulate this (base ,property), and returned. + * </p> + * + * <p> + * See the notes about comparison, serialization and immutability in the {@link Expression} javadocs. + * + * @see ELResolver + * @see Expression + * @see ExpressionFactory + * + * @since Jakarta Server Pages 2.1 + */ +public abstract class ValueExpression extends Expression { + + private static final long serialVersionUID = -8466802188968516519L; + + /** + * Evaluates the expression relative to the provided context, and returns the resulting value. + * + * <p> + * The resulting value is automatically coerced to the type returned by <code>getExpectedType()</code>, which was + * provided to the <code>ExpressionFactory</code> when this expression was created. + * + * @param context The context of this evaluation. + * + * @return The result of the expression evaluation. + * + * @throws NullPointerException if context is <code>null</code>. + * @throws PropertyNotFoundException if one of the property resolutions failed because a specified variable or property + * does not exist or is not readable. + * @throws ELException if an exception was thrown while performing property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if available. + */ + public abstract Object getValue(ELContext context); + + /** + * Evaluates the expression relative to the provided context, and sets the result to the provided value. + * + * @param context The context of this evaluation. + * @param value The new value to be set. + * + * @throws NullPointerException if context is <code>null</code>. + * @throws PropertyNotFoundException if one of the property resolutions failed because a specified variable or property + * does not exist or is not readable. + * @throws PropertyNotWritableException if the final variable or property resolution failed because the specified + * variable or property is not writable. + * @throws ELException if an exception was thrown while attempting to set the property or variable. The thrown exception + * must be included as the cause property of this exception, if available. + */ + public abstract void setValue(ELContext context, Object value); + + /** + * Evaluates the expression relative to the provided context, and returns <code>true</code> if a call to + * {@link #setValue} will always fail. + * + * @param context The context of this evaluation. + * + * @return <code>true</code> if the expression is read-only or <code>false</code> if not. + * + * @throws NullPointerException if context is <code>null</code>. + * @throws PropertyNotFoundException if one of the property resolutions failed because a specified variable or property + * does not exist or is not readable. + * @throws ELException if an exception was thrown while performing property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if available. * @throws NullPointerException if context is + * <code>null</code> + */ + public abstract boolean isReadOnly(ELContext context); + + /** + * Evaluates the expression relative to the provided context, and returns the most general type that is acceptable for + * an object to be passed as the <code>value</code> parameter in a future call to the {@link #setValue} method. + * + * <p> + * This is not always the same as <code>getValue().getClass()</code>. For example, in the case of an expression that + * references an array element, the <code>getType</code> method will return the element type of the array, which might + * be a superclass of the type of the actual element that is currently in the specified array element. + * + * @param context The context of this evaluation. + * + * @return the most general acceptable type; otherwise undefined. + * + * @throws NullPointerException if context is <code>null</code>. + * @throws PropertyNotFoundException if one of the property resolutions failed because a specified variable or property + * does not exist or is not readable. + * @throws ELException if an exception was thrown while performing property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if available. + */ + public abstract Class<?> getType(ELContext context); + + /** + * Returns the type the result of the expression will be coerced to after evaluation. + * + * @return the <code>expectedType</code> passed to the <code>ExpressionFactory.createValueExpression</code> method that + * created this <code>ValueExpression</code>. + */ + public abstract Class<?> getExpectedType(); + + /** + * Returns a {@link ValueReference} for this expression instance. + * + * @param context the context of this evaluation + * + * @return the <code>ValueReference</code> for this <code>ValueExpression</code>, or <code>null</code> if this + * <code>ValueExpression</code> is not a reference to a base (null or non-null) and a property. If the base is null, and + * the property is a Jakarta Expression Language variable, return the <code>ValueReference</code> for the + * <code>ValueExpression</code> associated with this Jakarta Expression Language variable. + * + * @since Jakarta Expression Language 2.2 + */ + public ValueReference getValueReference(ELContext context) { + return null; + } +}
diff --git a/api/src/main/java/javax/el/ValueReference.java b/api/src/main/java/javax/el/ValueReference.java new file mode 100644 index 0000000..d486c7f --- /dev/null +++ b/api/src/main/java/javax/el/ValueReference.java
@@ -0,0 +1,47 @@ +/* + * Copyright (c) 2010, 2018 Oracle and/or its affiliates and others. + * All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package javax.el; + +import java.io.Serializable; + +/** + * This encapsulates a base model object and one of its properties. + * + * @since Jakarta Expression Language 2.2 + */ +public class ValueReference implements Serializable { + + private static final long serialVersionUID = -4076659531951367109L; + + private Object base; + private Object property; + + public ValueReference(Object base, Object property) { + this.base = base; + this.property = property; + } + + public Object getBase() { + return base; + } + + public Object getProperty() { + return property; + } + +}
diff --git a/api/src/main/java/javax/el/VariableMapper.java b/api/src/main/java/javax/el/VariableMapper.java new file mode 100644 index 0000000..1b84214 --- /dev/null +++ b/api/src/main/java/javax/el/VariableMapper.java
@@ -0,0 +1,47 @@ +/* + * Copyright (c) 1997-2018 Oracle and/or its affiliates and others. + * All rights reserved. + * Copyright 2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package javax.el; + +/** + * The interface to a map between Jakarta Expression Language variables and the Jakarta Expression Language expressions + * they are associated with. + * + * @since Jakarta Server Pages 2.1 + */ +public abstract class VariableMapper { + + /** + * @param variable The variable name + * + * @return the ValueExpression assigned to the variable, null if there is no previous assignment to this variable. + */ + public abstract ValueExpression resolveVariable(String variable); + + /** + * Assign a ValueExpression to an Jakarta Expression Language variable, replacing any previously assignment to the same + * variable. The assignment for the variable is removed if the expression is <code>null</code>. + * + * @param variable The variable name + * @param expression The ValueExpression to be assigned to the variable. + * + * @return The previous ValueExpression assigned to this variable, null if there is no previous assignment to this + * variable. + */ + public abstract ValueExpression setVariable(String variable, ValueExpression expression); +}
diff --git a/api/src/main/java/javax/el/package.html b/api/src/main/java/javax/el/package.html new file mode 100644 index 0000000..22e6086 --- /dev/null +++ b/api/src/main/java/javax/el/package.html
@@ -0,0 +1,239 @@ +<!-- + + Copyright (c) 1997-2018 Oracle and/or its affiliates. All rights reserved. + Copyright 2004 The Apache Software Foundation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +--> + +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> +<head> +</head> + +<body bgcolor="white"> +Provides the API for <strong>Jakarta Expression Language 3.0</strong> + +<p>Jakarta Expression Language is a simple language originally designed to +satisfy the specific needs of web application developers. It has evolved +into its own specification intended for general use inside and outside of the +web containers.</p> + +<p>This package contains the classes and interfaces that describe +and define the programmatic access to the Jakarta Expression Language engine. +The API is logically partitioned as follows: + +<ul> + <li><a href="#Context">Jakarta Expression Language Context</a></li> + <li><a href="#ExpressionObjects">Expression Objects</a></li> + <li><a href="#ExpressionCreation">Creation of Expressions</a></li> + <li><a href="#ExpressionEvaluation">Evaluation of Expressions</a></li> + <li><a href="#EvaluationListener">Evaluation Listeners</a></li> + <li><a href="#Resolver">Resolution of Model Objects and their Properties</a></li> + <li><a href="#Functions">Jakarta Expression Language Functions</a></li> + <li><a href="#Variables">Jakarta Expression Language Variables</a></li> + <li><a href="#Standalone">Jakarta Expression Language in Stand-alone environment</a></li> +</ul> + +<h3><a name="Context">Jakarta Expression Language Context</a></h3> + +<p>An important goal of Jakarta Expression Language is to ensure it can be used in +a variety of environments. It must therefore provide enough flexibility +to adapt to the specific requirements of the environment where it is +being used.</p> + +<p>Class {@link javax.el.ELContext} is what links +the Jakarta Expression Language with the specific environment where it is being used. +It provides the mechanism through which all relevant context for creating or +evaluating an expression is specified. +</p> + +<p>When Jakarta Expression Language is used in a web container, the creation of <code>ELContext +</code> objects is controlled through the underlying technology. +For example, in Jakarta Server Pages, the + <code>JspContext.getELContext()</code> factory method is used. In an +stand-alone environment, a default {@link javax.el.StandardELContext} is +provided.</p> + + <p>Some technologies provide the ability to add an {@link javax.el.ELContextListener} + so that applications and frameworks can ensure their own context objects + are attached to any newly created <code>ELContext</code>.</p> + +<h3><a name="ExpressionObjects">Expression Objects</a></h3> + +<p>At the core of the Expression Language is the notion of an <i>expression</i> +that gets parsed according to the grammar defined by the Expression Language.</p> + +<p>There are two types of expressions defined by Jakarta Expression Language: <i>value expressions</i> +and <i>method expressions</i>. A {@link javax.el.ValueExpression} such as +<code>"${customer.name}"</code> can be used either +as an <i>rvalue</i> (return the value associated with property <code>name</code> +of the model object <code>customer</code>) or as an <i>lvalue</i> +(set the value of the property <code>name</code> of the model object +<code>customer</code>).</p> + +<p>A {@link javax.el.MethodExpression} such as +<code>"${handler.process}"</code> makes it possible to invoke a method +(<code>process</code>) on a specific model object (<code>handler</code>).</p> + +<p>In version 2.2 and later, either type of Jakarta Expression Language expression can represent a method +invocation, such as <code>${trader.buy("JAVA")}</code>, where the arguments to +the method invocation are specified in the expression.</p> + +<p>All expression classes extend the base class {@link javax.el.Expression}, making them +serializable and forcing them to implement <code>equals()</code> and +<code>hashCode()</code>. Moreover, each method on these expression classes +that actually evaluates an expression receives a parameter +of class {@link javax.el.ELContext}, +which provides the context required to evaluate the expression.</p> + +<h3><a name="ExpressionCreation">Creation of Expressions</a></h3> + +<p>An expression is created through the {@link javax.el.ExpressionFactory} class. +The factory provides two creation methods; one for each type of expression + supported by Jakarta Expression Language.</p> + +<p>To create an expression, one must provide an {@link javax.el.ELContext}, +a string representing +the expression, and the expected type (<code>ValueExpression</code>) or signature +(<code>MethodExpression</code>). + +The <code>ELContext</code> provides the context necessary to parse an expression. +Specifically, if the expression uses a Jakarta Expression Language function +(for example <code>${fn:toUpperCase(customer.name)}</code>) or a Jakarta Expression Language +variable, then {@link javax.el.FunctionMapper} and {@link javax.el.VariableMapper} +objects must be available within the <code>ELContext</code> so that Jakarta Expression Language +functions and Jakarta Expression Language variables are properly mapped. + +<h3><a name="ExpressionEvaluation">Evaluation of Expressions</a></h3> +<p>The creation and the evaluation of an expression are done in two separate +steps. At the evaluation of an expression, the {@link javax.el.ELContext} +provides the context necessary to support property and method resolution +for modal objects.</p> + +<p>A deferred expression is one that is created but not immediately evaluated. +In a Jakarta Faces request processing life cycle, Jakarta Expression Language +expressions are typically created in the tree building phase and evaluated in the +rendering phrase.</p> + +<p>Adding parameters to a <code>ValueExpression</code> further enhances the +power of deferred expressions. The {@link javax.el.LambdaExpression} +encapsulates such a construct. A <code>LambdaExpression</code> can be +invoked by supplying the actual parameters at evaluation. It plays +an important role in the support for collections operators.</p> + +<h3><a name="EvaluationListener">Evaluation Listeners</a></h3> +<p>By registering {@link javax.el.EvaluationListener}s in ELContext, a user can +receive notifications during the Jakarta Expression Language expression evaluations. +There are three events that trigger the notification: +<ul> + <li>Before evaluation</li> + <li>After evaluation</li> + <li>When (base, property) is resolved</li> +</ul></p> + +<h3><a name="Resolver">Resolution of Model Objects and their Properties</a></h3> + +<p>Through the {@link javax.el.ELResolver} base class, Jakarta Expression Language +features a pluggable mechanism to resolve model object references as well as properties +and method invocations of these objects.</p> + +<p>The Jakarta Expression Language API provides implementations of <code>ELResolver</code> supporting +property resolution for common data types which include arrays +({@link javax.el.ArrayELResolver}), JavaBeans ({@link javax.el.BeanELResolver}), <code>List</code>s ({@link javax.el.ListELResolver}), +<code>Map</code>s ({@link javax.el.MapELResolver}), and <code>ResourceBundle</code>s ({@link javax.el.ResourceBundleELResolver}).</p> + +<p>Tools can easily obtain more information about resolvable model objects and their +resolvable properties by calling +method <code>getFeatureDescriptors</code> on the <code>ELResolver</code>. This method exposes objects +of type <code>java.beans.FeatureDescriptor</code>, providing all information of interest +on top-level model objects as well as their properties.</p> + +<h3><a name="Functions">Jakarta Expression Language Functions</a></h3> + +<p>If a Jakarta Expression Language expression uses a function +(for example <code>${fn:toUpperCase(customer.name)}</code>), then a +{@link javax.el.FunctionMapper} object must also be specified within +the <code>ELContext</code>. + +The <code>FunctionMapper</code> is responsible to map + <code>${prefix:name()}</code> style functions to +static methods that can execute the specified functions. +</p> + +<h3><a name="Variables">Jakarta Expression Language Variables</a></h3> + +<p>Just like {@link javax.el.FunctionMapper} provides +a flexible mechanism to add functions to Jakarta Expression Language, +{@link javax.el.VariableMapper} provides a flexible mechanism to support the notion of +<strong>Jakarta Expression Language variables</strong>. +</p> + +<p> +A Jakarta Expression Language variable does not directly refer to a model object that can then +be resolved by an <code>ELResolver</code>. Instead, it refers to a Jakarta Expression Language +expression. The evaluation of that Jakarta Expression Language expression gives the +Jakarta Expression Language variable its value. +</p> + +<p> +For example, in the following code snippet +<blockquote> + <code><h:inputText value="#{handler.customer.name}"/></code> +</blockquote> + +<code>handler</code> refers to a model object that can be resolved by a Jakarta Expression Language Resolver. +</p> +<p> +However, in this other example: +<blockquote> +<pre> +<c:forEach var="item" items="#{model.list}"> + <h:inputText value="#{item.name}"/> +</c:forEach> +</pre> +</blockquote> + +<code>item</code> is a Jakarta Expression Language variable because it does not refer directly to a model +object. Instead, it refers to another Jakarta Expression Language expression, namely a +specific item in the collection referred to by the Jakarta Expression Language expression +<code>#{model.list}</code>. +</p> + +<p> +Assuming that there are three elements in <code>${model.list}</code>, this means +that for each invocation of <code><h:inputText></code>, the following information +about <code>item</code> must be preserved in the {@link javax.el.VariableMapper}: +<blockquote> + first invocation: <code>item</code> maps to first element in <code>${model.list}</code><br> + second invocation: <code>item</code> maps to second element in <code>${model.list}</code><br> + third invocation: <code>item</code> maps to third element in <code>${model.list}</code><br> +</blockquote> +<p> +<code>VariableMapper</code> provides the mechanisms required to allow the mapping +of a Jakarta Expression Language variable to the Jakarta Expression Language expression from which it gets its value. +</p> + +<h3><a name="Standalone">Jakarta Expression Language in Stand-alone environment</a></h3> +<p>Jakarta Expression Language 3.0 includes APIs for using Jakarta Expression Language in a stand-alone environment.</p> +<p>{@link javax.el.ELProcessor} provides simple APIs for the direct +evaluations of expressions. It also makes it easy to define functions, +set variables, and define beans locally.</p> + +<p>{@link javax.el.ELManager} provides lower level APIs for managing the Jakarta Expression Language +parsing and evaluation environment. It contains a default ELContext {@link javax.el.StandardELContext}.</p> + +</body> +</html> +
diff --git a/api/src/main/javadoc/doc-files/speclicense.html b/api/src/main/javadoc/doc-files/speclicense.html new file mode 100644 index 0000000..7c404e0 --- /dev/null +++ b/api/src/main/javadoc/doc-files/speclicense.html
@@ -0,0 +1,72 @@ +<html> +<head> +<title>Eclipse Foundation Specification License - v1.0</title> +</head> +<body> +<h1>Eclipse Foundation Specification License - v1.0</h1> +<p>By using and/or copying this document, or the Eclipse Foundation + document from which this statement is linked, you (the licensee) agree + that you have read, understood, and will comply with the following + terms and conditions:</p> + +<p>Permission to copy, and distribute the contents of this document, or + the Eclipse Foundation document from which this statement is linked, in + any medium for any purpose and without fee or royalty is hereby + granted, provided that you include the following on ALL copies of the + document, or portions thereof, that you use:</p> + +<ul> + <li> link or URL to the original Eclipse Foundation document.</li> + <li>All existing copyright notices, or if one does not exist, a notice + (hypertext is preferred, but a textual representation is permitted) + of the form: "Copyright © [$date-of-document] + “Eclipse Foundation, Inc. <<url to this license>> + " + </li> +</ul> + +<p>Inclusion of the full text of this NOTICE must be provided. We + request that authorship attribution be provided in any software, + documents, or other items or products that you create pursuant to the + implementation of the contents of this document, or any portion + thereof.</p> + +<p>No right to create modifications or derivatives of Eclipse Foundation + documents is granted pursuant to this license, except anyone may + prepare and distribute derivative works and portions of this document + in software that implements the specification, in supporting materials + accompanying such software, and in documentation of such software, + PROVIDED that all such works include the notice below. HOWEVER, the + publication of derivative works of this document for use as a technical + specification is expressly prohibited.</p> + +<p>The notice is:</p> + +<p>"Copyright © 2018 Eclipse Foundation. This software or + document includes material copied from or derived from [title and URI + of the Eclipse Foundation specification document]."</p> + +<h2>Disclaimers</h2> + +<p>THIS DOCUMENT IS PROVIDED "AS IS," AND THE COPYRIGHT + HOLDERS AND THE ECLIPSE FOUNDATION MAKE NO REPRESENTATIONS OR + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, + NON-INFRINGEMENT, OR TITLE; THAT THE CONTENTS OF THE DOCUMENT ARE + SUITABLE FOR ANY PURPOSE; NOR THAT THE IMPLEMENTATION OF SUCH CONTENTS + WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR + OTHER RIGHTS.</p> + +<p>THE COPYRIGHT HOLDERS AND THE ECLIPSE FOUNDATION WILL NOT BE LIABLE + FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT + OF ANY USE OF THE DOCUMENT OR THE PERFORMANCE OR IMPLEMENTATION OF THE + CONTENTS THEREOF.</p> + +<p>The name and trademarks of the copyright holders or the Eclipse + Foundation may NOT be used in advertising or publicity pertaining to + this document or its contents without specific, written prior + permission. Title to copyright in this document will at all times + remain with copyright holders.</p> + +</body> +</html> \ No newline at end of file
diff --git a/exclude.xml b/exclude.xml new file mode 100644 index 0000000..e25e681 --- /dev/null +++ b/exclude.xml
@@ -0,0 +1,35 @@ +<!-- + + Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + + This program and the accompanying materials are made available under the + terms of the Eclipse Public License v. 2.0, which is available at + http://www.eclipse.org/legal/epl-2.0. + + This Source Code may also be made available under the following Secondary + Licenses when the conditions for such availability set forth in the + Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + version 2 with the GNU Classpath Exception, which is available at + https://www.gnu.org/software/classpath/license.html. + + SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +--> + +<FindBugsFilter> + <Match> + <Class name="com.sun.el.parser.SimpleCharStream"/> + </Match> + <Match> + <Class name="com.sun.el.parser.ELParser"/> + </Match> + <Match> + <Class name="com.sun.el.parser.ELParserTokenManager"/> + </Match> + <Match> + <Class name="com.sun.el.parser.ELParserConstants"/> + </Match> + <Match> + <Class name="com.sun.el.parser.TokenMgrError"/> + </Match> +</FindBugsFilter>
diff --git a/impl/build.xml b/impl/build.xml new file mode 100644 index 0000000..2ed734c --- /dev/null +++ b/impl/build.xml
@@ -0,0 +1,45 @@ +<!-- + + Copyright (c) 2010, 2018 Oracle and/or its affiliates. All rights reserved. + + This program and the accompanying materials are made available under the + terms of the Eclipse Public License v. 2.0, which is available at + http://www.eclipse.org/legal/epl-2.0. + + This Source Code may also be made available under the following Secondary + Licenses when the conditions for such availability set forth in the + Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + version 2 with the GNU Classpath Exception, which is available at + https://www.gnu.org/software/classpath/license.html. + + SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +--> + +<!-- Use ant to generate .jj and .java files. Use mvn for build. --> + +<project name="el-impl" default="generate" basedir="."> + + <property name="javacc.home" value="/Users/kichung/tools/javacc-5.0"/> + <property name="dir" value="src/main/java/com/sun/el/parser"/> + + <target name="generate" description="Generate java files"> + + <jjtree target="${dir}/ELParser.jjt" + outputdirectory="${dir}" + javacchome="${javacc.home}"/> + <javacc target="${dir}/ELParser.jj" + outputdirectory="${dir}" + javacchome="${javacc.home}"/> + + <replaceregexp byline="true"> + <regexp pattern="final private LookaheadSuccess"/> + <substitution expression="static final private LookaheadSuccess"/> + <fileset dir="src/main/java/com/sun/el/parser"> + <include name="ELParser.java"/> + </fileset> + </replaceregexp> + + </target> +</project> +
diff --git a/impl/pom.xml b/impl/pom.xml new file mode 100644 index 0000000..9c28069 --- /dev/null +++ b/impl/pom.xml
@@ -0,0 +1,276 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Copyright (c) 1997, 2018 Oracle and/or its affiliates and others. + All rights reserved. + + This program and the accompanying materials are made available under the + terms of the Eclipse Public License v. 2.0, which is available at + http://www.eclipse.org/legal/epl-2.0. + + This Source Code may also be made available under the following Secondary + Licenses when the conditions for such availability set forth in the + Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + version 2 with the GNU Classpath Exception, which is available at + https://www.gnu.org/software/classpath/license.html. + + SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.eclipse.ee4j</groupId> + <artifactId>project</artifactId> + <version>1.0.5</version> + <relativePath/> + </parent> + + <groupId>com.sun.el</groupId> + <artifactId>el-ri</artifactId> + <version>3.0.3-SNAPSHOT</version> + + <name>Jakarta Expression Language 3.0 Implementation</name> + <description>Jakarta Expression Language Implementation</description> + <url>https://projects.eclipse.org/projects/ee4j.el</url> + + <properties> + <!-- the bundle build number must be the same as the maven number --> + <bundle.version>3.0.3</bundle.version> + <!-- The most current api version --> + <spec.version>3.0</spec.version> + <extensionName>javax.el.impl</extensionName> + <bundle.symbolicName>com.sun.el.javax.el</bundle.symbolicName> + <vendorName>Oracle Corporation</vendorName> + <findbugs.version>2.5.2</findbugs.version> + <findbugs.exclude>${project.basedir}/exclude.xml</findbugs.exclude> + <findbugs.threshold>High</findbugs.threshold> + <tlda-license.url>http://hudson-sca.us.oracle.com/job/tlda-license/lastSuccessfulBuild/artifact</tlda-license.url> + </properties> + + <issueManagement> + <system>github</system> + <url>https://github.com/eclipse-ee4j/el-ri/issues</url> + </issueManagement> + + <licenses> + <license> + <name>EPL 2.0</name> + <url>http://www.eclipse.org/legal/epl-2.0</url> + <distribution>repo</distribution> + </license> + <license> + <name>GPL2 w/ CPE</name> + <url>https://www.gnu.org/software/classpath/license.html</url> + <distribution>repo</distribution> + </license> + </licenses> + + <mailingLists> + <mailingList> + <name>Jakarta Expression Language 3.0 mailing list</name> + <post>el-dev@eclipse.org</post> + <subscribe>https://dev.eclipse.org/mailman/listinfo/el-dev</subscribe> + <unsubscribe>https://dev.eclipse.org/mailman/listinfo/el-dev</unsubscribe> + <archive>https://dev.eclipse.org/mhonarc/lists/el-dev</archive> + </mailingList> + </mailingLists> + + <scm> + <connection>scm:git:https://github.com/eclipse-ee4j/el-ri.git</connection> + <developerConnection> + scm:git:git@github.com:eclipse-ee4j/el-ri.git + </developerConnection> + <url>https://github.com/eclipse-ee4j/el-ri</url> + <tag>HEAD</tag> + </scm> + + <developers> + <developer> + <id>yaminikb</id> + <name>Yamini K B</name> + <organization>Oracle Corporation</organization> + <organizationUrl>http://www.oracle.com/</organizationUrl> + </developer> + </developers> + + <contributors> + <contributor> + <name>Kin-man Chung</name> + </contributor> + </contributors> + + <build> + <resources> + <resource> + <directory>src/main/java</directory> + <includes> + <include>**/*.properties</include> + <include>**/*.xml</include> + </includes> + </resource> + <resource> + <directory>${project.basedir}/..</directory> + <includes> + <include>LICENSE.md</include> + <include>NOTICE.md</include> + </includes> + <targetPath>META-INF</targetPath> + </resource> + </resources> + + + <plugins> + <!-- Configure maven-bundle-plugin to generate OSGi manifest. Please note: we use the manifest goal only and not the bundle goal. + The bundle goal can lead to very surprising results if the package names are not correctly specified. So, we use the jar plugin to generate the + jar. --> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <version>1.4.3</version> + <configuration> + <instructions> + <Bundle-SymbolicName>${bundle.symbolicName}</Bundle-SymbolicName> + <Bundle-Description> + Jakarta Expression Language ${spec.version} Implementation + </Bundle-Description> + <Bundle-Version>${bundle.version}</Bundle-Version> + <Extension-Name>${extensionName}</Extension-Name> + <Specification-Version>${spec.version}</Specification-Version> + <Specification-Vendor>${vendorName}</Specification-Vendor> + <Implementation-Version>${project.version}</Implementation-Version> + <Implementation-Vendor>${vendorName}</Implementation-Vendor> + <Export-Package>com.sun.el</Export-Package> + </instructions> + </configuration> + <executions> + <execution> + <id>bundle-manifest</id> + <phase>process-classes</phase> + <goals> + <goal>manifest</goal> + </goals> + </execution> + </executions> + </plugin> + + <plugin> + <artifactId>maven-jar-plugin</artifactId> + <version>2.4</version> + <configuration> + <archive> + <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile> + </archive> + </configuration> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.0</version> + <configuration> + <source>1.7</source> + <target>1.7</target> + <compilerArgument>-Xlint:unchecked</compilerArgument> + </configuration> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-javadoc-plugin</artifactId> + <version>3.0.1</version> + <executions> + <execution> + <id>attach-javadocs</id> + <goals> + <goal>jar</goal> + </goals> + <configuration> + <additionalJOption>-Xdoclint:none</additionalJOption> + <groups> + <group> + <title>Jakarta Expression Language 3.0 Implementation</title> + <packages>com.sun.el</packages> + </group> + </groups> + <bottom> Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved. Use is subject to license terms. </bottom> + </configuration> + </execution> + </executions> + </plugin> + + <!-- Use ant to manually invoke javacc, as this required is very infrequently <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>javacc-maven-plugin</artifactId> + <version>2.6</version> <executions> <execution> <id>jjtree-javacc</id> <goals> <goal>jjtree-javacc</goal> </goals> <configuration> <sourceDirectory>src/main/java/com/sun/el/parser</sourceDirectory> + <outputDirectory>src/main/java/com/sun/el/parser</outputDirectory> </configuration> </execution> </executions> </plugin> --> + + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>findbugs-maven-plugin</artifactId> + <version>${findbugs.version}</version> + <configuration> + <threshold>${findbugs.threshold}</threshold> + <excludeFilterFile>${findbugs.exclude}</excludeFilterFile> + <findbugsXmlOutput>true</findbugsXmlOutput> + <findbugsXmlWithMessages>true</findbugsXmlWithMessages> + </configuration> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-release-plugin</artifactId> + <configuration> + <mavenExecutorId>forked-path</mavenExecutorId> + <useReleaseProfile>false</useReleaseProfile> + <arguments>${release.arguments}</arguments> + </configuration> + </plugin> + + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <version>2.7.1</version> + <dependencies> + <dependency> + <groupId>org.apache.maven.surefire</groupId> + <artifactId>surefire-junit47</artifactId> + <version>2.7.1</version> + </dependency> + </dependencies> + <configuration> + <forkMode>never</forkMode> + </configuration> + </plugin> + </plugins> + </build> + + <reporting> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>findbugs-maven-plugin</artifactId> + <version>${findbugs.version}</version> + <configuration> + <threshold>${findbugs.threshold}</threshold> + <excludeFilterFile>${findbugs.exclude}</excludeFilterFile> + </configuration> + </plugin> + </plugins> + </reporting> + + <dependencies> + <dependency> + <groupId>jakarta.el</groupId> + <artifactId>jakarta.el-api</artifactId> + <version>${bundle.version}</version> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.12</version> + <scope>test</scope> + </dependency> + </dependencies> + +</project>
diff --git a/impl/src/main/java/com/sun/el/ExpressionFactoryImpl.java b/impl/src/main/java/com/sun/el/ExpressionFactoryImpl.java new file mode 100644 index 0000000..ee9a042 --- /dev/null +++ b/impl/src/main/java/com/sun/el/ExpressionFactoryImpl.java
@@ -0,0 +1,115 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import javax.el.ELContext; +import javax.el.ELException; +import javax.el.ELResolver; +import javax.el.ExpressionFactory; +import javax.el.MethodExpression; +import javax.el.ValueExpression; + +import com.sun.el.lang.ELSupport; +import com.sun.el.lang.ExpressionBuilder; +import com.sun.el.stream.StreamELResolver; +import com.sun.el.util.MessageFactory; + +/** + * @see javax.el.ExpressionFactory + * + * @author Jacob Hookom [jacob@hookom.net] + * @author Kin-man Chung + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public class ExpressionFactoryImpl extends ExpressionFactory { + + private Properties properties; + private boolean isBackwardCompatible22; + + public ExpressionFactoryImpl() { + super(); + } + + public ExpressionFactoryImpl(Properties properties) { + super(); + this.properties = properties; + this.isBackwardCompatible22 = "true".equals(getProperty("javax.el.bc2.2")); + } + + @Override + public Object coerceToType(Object obj, Class<?> type) { + try { + return ELSupport.coerceToType(obj, type, isBackwardCompatible22); + } catch (IllegalArgumentException ex) { + throw new ELException(ex); + } + } + + @Override + public MethodExpression createMethodExpression(ELContext context, String expression, Class<?> expectedReturnType, Class<?>[] expectedParamTypes) { + MethodExpression methodExpression = + new ExpressionBuilder(expression, context) + .createMethodExpression(expectedReturnType, expectedParamTypes); + + if (expectedParamTypes == null && !methodExpression.isParametersProvided()) { + throw new NullPointerException(MessageFactory.get("error.method.nullParms")); + } + + return methodExpression; + } + + @Override + public ValueExpression createValueExpression(ELContext context, String expression, Class<?> expectedType) { + if (expectedType == null) { + throw new NullPointerException(MessageFactory.get("error.value.expectedType")); + } + + return new ExpressionBuilder(expression, context).createValueExpression(expectedType); + } + + @Override + public ValueExpression createValueExpression(Object instance, Class<?> expectedType) { + if (expectedType == null) { + throw new NullPointerException(MessageFactory.get("error.value.expectedType")); + } + + return new ValueExpressionLiteral(instance, expectedType); + } + + public String getProperty(String key) { + if (properties == null) { + return null; + } + + return properties.getProperty(key); + } + + @Override + public ELResolver getStreamELResolver() { + return new StreamELResolver(); + } + + @Override + public Map<String, Method> getInitFunctionMap() { + return new HashMap<String, Method>(); + } +}
diff --git a/impl/src/main/java/com/sun/el/Messages.properties b/impl/src/main/java/com/sun/el/Messages.properties new file mode 100644 index 0000000..802a38a --- /dev/null +++ b/impl/src/main/java/com/sun/el/Messages.properties
@@ -0,0 +1,79 @@ +# +# Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. +# +# This program and the accompanying materials are made available under the +# terms of the Eclipse Public License v. 2.0, which is available at +# http://www.eclipse.org/legal/epl-2.0. +# +# This Source Code may also be made available under the following Secondary +# Licenses when the conditions for such availability set forth in the +# Eclipse Public License v. 2.0 are satisfied: GNU General Public License, +# version 2 with the GNU Classpath Exception, which is available at +# https://www.gnu.org/software/classpath/license.html. +# +# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 +# + +# General Errors +error.convert=Cannot convert {0} of type {1} to {2} +error.compare=Cannot compare {0} to {1} +error.function=Problems calling function ''{0}'' +error.function.syntax=Syntax error in calling function ''{0}'' +error.unreachable.base=Target Unreachable, identifier ''{0}'' resolved to null +error.unreachable.property=Target Unreachable, ''{0}'' returned null +error.resolver.unhandled=ELResolver did not handle type: {0} with property of ''{1}'' +error.resolver.unhandled.null=ELResolver cannot handle a null base Object with identifier ''{0}'' + +# ValueExpressionLiteral +error.value.literal.write=ValueExpression is a literal and not writable: {0} + +# ExpressionFactoryImpl +error.null=Expression cannot be null +error.mixed=Expression cannot contain both '#{..}' and '${..}' : {0} +error.method=Not a valid MethodExpression : {0} +error.method.nullParms=Parameter types cannot be null +error.value.expectedType=Expected type cannot be null + +# ExpressionMediator +error.eval=Error Evaluating {0} : {1} + +# ValueSetVisitor +error.syntax.set=Illegal Syntax for Set Operation + +error.syntax.assign=Illegal Syntax for Assign Operation + +# ReflectionUtil +error.method.notfound=Method not found: {0}.{1}({2}) +error.method.ambiguous=Unable to find unambiguous method: {0}.{1}({2}) +error.property.notfound=Property ''{1}'' not found on {0} + +# ValidatingVisitor +error.fnMapper.null=Expression uses functions, but no FunctionMapper was provided +error.fnMapper.method=Function ''{0}'' not found +error.fnMapper.paramcount=Function ''{0}'' specifies {1} params, but {2} were supplied + +# **ExpressionImpl +error.context.null=ELContext was null + +# ArrayELResolver +error.array.outofbounds=Index {0} is out of bounds for array of size {1} + +# ListELResolver +error.list.outofbounds=Index {0} is out of bounds for list of size {1} + +# BeanELResolver +error.property.notfound=Property ''{1}'' not found on type: {0} +error.property.invocation=Property ''{1}'' threw an exception from type: {0} +error.property.notreadable=Property ''{1}'' doesn't have a 'get' specified on type: {0} +error.property.notwritable=Property ''{1}'' doesn't have a 'set' specified on type: {0} + +# AstValue +error.method.name=An instance of {0} is specified as the static method name, it +must be a String + +# AstType +error.class.notfound=The specified class ''{0}'' not found + +# AstLambdaExpression +error.lambda.call=A Lambda expression must return another Lambda expression in this syntax +error.lambda.parameter.readonly=The Lambda parameter ''{0}'' is not writable
diff --git a/impl/src/main/java/com/sun/el/MethodExpressionImpl.java b/impl/src/main/java/com/sun/el/MethodExpressionImpl.java new file mode 100644 index 0000000..fb0aea4 --- /dev/null +++ b/impl/src/main/java/com/sun/el/MethodExpressionImpl.java
@@ -0,0 +1,285 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el; + +import static com.sun.el.util.ReflectionUtil.forName; +import static com.sun.el.util.ReflectionUtil.toTypeArray; +import static com.sun.el.util.ReflectionUtil.toTypeNameArray; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +import javax.el.ELContext; +import javax.el.ELException; +import javax.el.ELResolver; +import javax.el.Expression; +import javax.el.ExpressionFactory; +import javax.el.FunctionMapper; +import javax.el.MethodExpression; +import javax.el.MethodInfo; +import javax.el.MethodNotFoundException; +import javax.el.PropertyNotFoundException; +import javax.el.VariableMapper; + +import com.sun.el.lang.EvaluationContext; +import com.sun.el.lang.ExpressionBuilder; +import com.sun.el.parser.Node; + +/** + * An <code>Expression</code> that refers to a method on an object. + * + * <p> + * The {@link ExpressionFactory#createMethodExpression} method can be used to parse an expression string and return a + * concrete instance of <code>MethodExpression</code> that encapsulates the parsed expression. The + * {@link FunctionMapper} is used at parse time, not evaluation time, so one is not needed to evaluate an expression + * using this class. However, the {@link ELContext} is needed at evaluation time. + * </p> + * + * <p> + * The {@link #getMethodInfo} and {@link #invoke} methods will evaluate the expression each time they are called. The + * {@link ELResolver} in the <code>ELContext</code> is used to resolve the top-level variables and to determine the + * behavior of the <code>.</code> and <code>[]</code> operators. For any of the two methods, the + * {@link ELResolver#getValue} method is used to resolve all properties up to but excluding the last one. This provides + * the <code>base</code> object on which the method appears. If the <code>base</code> object is null, a + * <code>NullPointerException</code> must be thrown. At the last resolution, the final <code>property</code> is then + * coerced to a <code>String</code>, which provides the name of the method to be found. A method matching the name and + * expected parameters provided at parse time is found and it is either queried or invoked (depending on the method + * called on this <code>MethodExpression</code>). + * </p> + * + * <p> + * See the notes about comparison, serialization and immutability in the {@link Expression} javadocs. + * + * @see javax.el.ELResolver + * @see javax.el.Expression + * @see javax.el.ExpressionFactory + * @see javax.el.MethodExpression + * + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class MethodExpressionImpl extends MethodExpression implements Externalizable { + + private Class<?> expectedType; + private String expr; + private FunctionMapper fnMapper; + private VariableMapper varMapper; + private Class<?>[] paramTypes; + + private transient Node node; + + public MethodExpressionImpl() { + super(); + } + + /** + * @param expr the expression + * @param node the node + * @param fnMapper the function mapper + * @param varMapper the variable mapper + * @param expectedType expected return type of method + * @param paramTypes the method parameters + */ + public MethodExpressionImpl(String expr, Node node, FunctionMapper fnMapper, VariableMapper varMapper, Class<?> expectedType, Class<?>[] paramTypes) { + super(); + this.expr = expr; + this.node = node; + this.fnMapper = fnMapper; + this.varMapper = varMapper; + this.expectedType = expectedType; + this.paramTypes = paramTypes; + } + + /** + * Determines whether the specified object is equal to this <code>Expression</code>. + * + * <p> + * The result is <code>true</code> if and only if the argument is not <code>null</code>, is an <code>Expression</code> + * object that is the of the same type (<code>ValueExpression</code> or <code>MethodExpression</code>), and has an + * identical parsed representation. + * </p> + * + * <p> + * Note that two expressions can be equal if their expression Strings are different. For example, + * <code>${fn1:foo()}</code> and <code>${fn2:foo()}</code> are equal if their corresponding <code>FunctionMapper</code>s + * mapped <code>fn1:foo</code> and <code>fn2:foo</code> to the same method. + * </p> + * + * @param obj the <code>Object</code> to test for equality. + * @return <code>true</code> if <code>obj</code> equals this <code>Expression</code>; <code>false</code> otherwise. + * @see java.util.Hashtable + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof MethodExpressionImpl) { + MethodExpressionImpl methodExpressionImpl = (MethodExpressionImpl) obj; + return getNode().equals(methodExpressionImpl.getNode()); + } + + return false; + } + + /** + * Returns the original String used to create this <code>Expression</code>, unmodified. + * + * <p> + * This is used for debugging purposes but also for the purposes of comparison (e.g. to ensure the expression in a + * configuration file has not changed). + * </p> + * + * <p> + * This method does not provide sufficient information to re-create an expression. Two different expressions can have + * exactly the same expression string but different function mappings. Serialization should be used to save and restore + * the state of an <code>Expression</code>. + * </p> + * + * @return The original expression String. + * + * @see javax.el.Expression#getExpressionString() + */ + @Override + public String getExpressionString() { + return expr; + } + + /** + * Evaluates the expression relative to the provided context, and returns information about the actual referenced + * method. + * + * @param context The context of this evaluation + * @return an instance of <code>MethodInfo</code> containing information about the method the expression evaluated to. + * @throws NullPointerException if context is <code>null</code> or the base object is <code>null</code> on the last + * resolution. + * @throws PropertyNotFoundException if one of the property resolutions failed because a specified variable or property + * does not exist or is not readable. + * @throws MethodNotFoundException if no suitable method can be found. + * @throws ELException if an exception was thrown while performing property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if available. + * @see javax.el.MethodExpression#getMethodInfo(javax.el.ELContext) + */ + @Override + public MethodInfo getMethodInfo(ELContext context) throws PropertyNotFoundException, MethodNotFoundException, ELException { + return getNode().getMethodInfo(new EvaluationContext(context, fnMapper, varMapper), paramTypes); + } + + /** + * @return The Node for the expression + * @throws ELException + */ + private Node getNode() throws ELException { + if (node == null) { + node = ExpressionBuilder.createNode(expr); + } + + return node; + } + + /** + * Returns the hash code for this <code>Expression</code>. + * + * <p> + * See the note in the {@link #equals} method on how two expressions can be equal if their expression Strings are + * different. Recall that if two objects are equal according to the <code>equals(Object)</code> method, then calling the + * <code>hashCode</code> method on each of the two objects must produce the same integer result. Implementations must + * take special note and implement <code>hashCode</code> correctly. + * </p> + * + * @return The hash code for this <code>Expression</code>. + * @see #equals + * @see java.util.Hashtable + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return getNode().hashCode(); + } + + /** + * Evaluates the expression relative to the provided context, invokes the method that was found using the supplied + * parameters, and returns the result of the method invocation. + * + * @param context The context of this evaluation. + * @param params The parameters to pass to the method, or <code>null</code> if no parameters. + * @return the result of the method invocation (<code>null</code> if the method has a <code>void</code> return type). + * @throws NullPointerException if context is <code>null</code> or the base object is <code>null</code> on the last + * resolution. + * @throws PropertyNotFoundException if one of the property resolutions failed because a specified variable or property + * does not exist or is not readable. + * @throws MethodNotFoundException if no suitable method can be found. + * @throws ELException if an exception was thrown while performing property or variable resolution. The thrown exception + * must be included as the cause property of this exception, if available. If the exception thrown is an + * <code>InvocationTargetException</code>, extract its <code>cause</code> and pass it to the <code>ELException</code> + * constructor. + * @see javax.el.MethodExpression#invoke(javax.el.ELContext, java.lang.Object[]) + */ + @Override + public Object invoke(ELContext context, Object[] params) throws PropertyNotFoundException, MethodNotFoundException, ELException { + EvaluationContext ctx = new EvaluationContext(context, fnMapper, varMapper); + ctx.notifyBeforeEvaluation(expr); + + Object obj = getNode().invoke(ctx, paramTypes, params); + + ctx.notifyAfterEvaluation(expr); + return obj; + } + + /* + * (non-Javadoc) + * + * @see java.io.Externalizable#readExternal(java.io.ObjectInput) + */ + @Override + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + expr = in.readUTF(); + String type = in.readUTF(); + + if (!"".equals(type)) { + expectedType = forName(type); + } + + paramTypes = toTypeArray(((String[]) in.readObject())); + fnMapper = (FunctionMapper) in.readObject(); + varMapper = (VariableMapper) in.readObject(); + } + + /* + * (non-Javadoc) + * + * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput) + */ + @Override + public void writeExternal(ObjectOutput out) throws IOException { + out.writeUTF(expr); + out.writeUTF(expectedType != null ? expectedType.getName() : ""); + out.writeObject(toTypeNameArray(paramTypes)); + out.writeObject(fnMapper); + out.writeObject(varMapper); + } + + @Override + public boolean isLiteralText() { + return false; + } + + @Override + public boolean isParametersProvided() { + return getNode().isParametersProvided(); + } +}
diff --git a/impl/src/main/java/com/sun/el/MethodExpressionLiteral.java b/impl/src/main/java/com/sun/el/MethodExpressionLiteral.java new file mode 100644 index 0000000..ae2b5b6 --- /dev/null +++ b/impl/src/main/java/com/sun/el/MethodExpressionLiteral.java
@@ -0,0 +1,106 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el; + +import static com.sun.el.util.ReflectionUtil.toTypeArray; +import static com.sun.el.util.ReflectionUtil.toTypeNameArray; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +import javax.el.ELContext; +import javax.el.ELException; +import javax.el.MethodExpression; +import javax.el.MethodInfo; + +import com.sun.el.util.ReflectionUtil; + +public class MethodExpressionLiteral extends MethodExpression implements Externalizable { + + private Class<?> expectedType; + private String expr; + private Class<?>[] paramTypes; + + public MethodExpressionLiteral() { + // do nothing + } + + public MethodExpressionLiteral(String expr, Class<?> expectedType, Class<?>[] paramTypes) { + this.expr = expr; + this.expectedType = expectedType; + this.paramTypes = paramTypes; + } + + @Override + public MethodInfo getMethodInfo(ELContext context) throws ELException { + return new MethodInfo(expr, expectedType, paramTypes); + } + + @Override + public Object invoke(ELContext context, Object[] params) throws ELException { + if (expectedType == null) { + return expr; + } + + try { + return context.convertToType(expr, expectedType); + } catch (Exception ex) { + throw new ELException(ex); + } + } + + @Override + public String getExpressionString() { + return expr; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof MethodExpressionLiteral && this.hashCode() == obj.hashCode(); + } + + @Override + public int hashCode() { + return expr.hashCode(); + } + + @Override + public boolean isLiteralText() { + return true; + } + + @Override + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + expr = in.readUTF(); + String type = in.readUTF(); + + if (!"".equals(type)) { + expectedType = ReflectionUtil.forName(type); + } + + paramTypes = toTypeArray(((String[]) in.readObject())); + } + + @Override + public void writeExternal(ObjectOutput out) throws IOException { + out.writeUTF(expr); + out.writeUTF(expectedType != null ? expectedType.getName() : ""); + out.writeObject(toTypeNameArray(paramTypes)); + } +}
diff --git a/impl/src/main/java/com/sun/el/ValueExpressionImpl.java b/impl/src/main/java/com/sun/el/ValueExpressionImpl.java new file mode 100644 index 0000000..7f476eb --- /dev/null +++ b/impl/src/main/java/com/sun/el/ValueExpressionImpl.java
@@ -0,0 +1,263 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el; + +import static com.sun.el.util.ReflectionUtil.forName; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +import javax.el.ELContext; +import javax.el.ELException; +import javax.el.ELResolver; +import javax.el.Expression; +import javax.el.ExpressionFactory; +import javax.el.FunctionMapper; +import javax.el.PropertyNotFoundException; +import javax.el.PropertyNotWritableException; +import javax.el.ValueExpression; +import javax.el.ValueReference; +import javax.el.VariableMapper; + +import com.sun.el.lang.EvaluationContext; +import com.sun.el.lang.ExpressionBuilder; +import com.sun.el.parser.AstLiteralExpression; +import com.sun.el.parser.Node; + +/** + * An <code>Expression</code> that can get or set a value. + * + * <p> + * In previous incarnations of this API, expressions could only be read. <code>ValueExpression</code> objects can now be + * used both to retrieve a value and to set a value. Expressions that can have a value set on them are referred to as + * l-value expressions. Those that cannot are referred to as r-value expressions. Not all r-value expressions can be + * used as l-value expressions (e.g. <code>"${1+1}"</code> or <code>"${firstName} ${lastName}"</code>). See the EL + * Specification for details. Expressions that cannot be used as l-values must always return <code>true</code> from + * <code>isReadOnly()</code>. + * </p> + * + * <p> + * The {@link ExpressionFactory#createValueExpression} method can be used to parse an expression string and return a + * concrete instance of <code>ValueExpression</code> that encapsulates the parsed expression. The {@link FunctionMapper} + * is used at parse time, not evaluation time, so one is not needed to evaluate an expression using this class. However, + * the {@link ELContext} is needed at evaluation time. + * </p> + * + * <p> + * The {@link #getValue}, {@link #setValue}, {@link #isReadOnly} and {@link #getType} methods will evaluate the + * expression each time they are called. The {@link ELResolver} in the <code>ELContext</code> is used to resolve the + * top-level variables and to determine the behavior of the <code>.</code> and <code>[]</code> operators. For any of the + * four methods, the {@link ELResolver#getValue} method is used to resolve all properties up to but excluding the last + * one. This provides the <code>base</code> object. At the last resolution, the <code>ValueExpression</code> will call + * the corresponding {@link ELResolver#getValue}, {@link ELResolver#setValue}, {@link ELResolver#isReadOnly} or + * {@link ELResolver#getType} method, depending on which was called on the <code>ValueExpression</code>. + * </p> + * + * <p> + * See the notes about comparison, serialization and immutability in the {@link Expression} javadocs. + * + * @see javax.el.ELResolver + * @see javax.el.Expression + * @see javax.el.ExpressionFactory + * @see javax.el.ValueExpression + * + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: dochez $ + */ +public final class ValueExpressionImpl extends ValueExpression implements Externalizable { + + private Class<?> expectedType; + private String expr; + private FunctionMapper fnMapper; + private VariableMapper varMapper; + private transient Node node; + + public ValueExpressionImpl() { + + } + + public ValueExpressionImpl(String expr, Node node, FunctionMapper fnMapper, VariableMapper varMapper, Class<?> expectedType) { + this.expr = expr; + this.node = node; + this.fnMapper = fnMapper; + this.varMapper = varMapper; + this.expectedType = expectedType; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof ValueExpressionImpl) { + ValueExpressionImpl valueExpressionImpl = (ValueExpressionImpl) obj; + return getNode().equals(valueExpressionImpl.getNode()); + } + + return false; + } + + /* + * (non-Javadoc) + * + * @see javax.el.ValueExpression#getExpectedType() + */ + @Override + public Class<?> getExpectedType() { + return expectedType; + } + + /** + * Returns the type the result of the expression will be coerced to after evaluation. + * + * @return the <code>expectedType</code> passed to the <code>ExpressionFactory.createValueExpression</code> method that + * created this <code>ValueExpression</code>. + * + * @see javax.el.Expression#getExpressionString() + */ + @Override + public String getExpressionString() { + return expr; + } + + /** + * @return The Node for the expression + * @throws ELException + */ + private Node getNode() throws ELException { + if (node == null) { + node = ExpressionBuilder.createNode(expr); + } + + return this.node; + } + + /* + * (non-Javadoc) + * + * @see javax.el.ValueExpression#getType(javax.el.ELContext) + */ + @Override + public Class<?> getType(ELContext context) throws PropertyNotFoundException, ELException { + return getNode().getType(new EvaluationContext(context, fnMapper, varMapper)); + } + + /* + * (non-Javadoc) + * + * @see javax.el.ValueExpression#getValueReference(javax.el.ELContext) + */ + @Override + public ValueReference getValueReference(ELContext context) throws PropertyNotFoundException, ELException { + return getNode().getValueReference(new EvaluationContext(context, fnMapper, varMapper)); + } + + /* + * (non-Javadoc) + * + * @see javax.el.ValueExpression#getValue(javax.el.ELContext) + */ + @Override + public Object getValue(ELContext context) throws PropertyNotFoundException, ELException { + EvaluationContext ctx = new EvaluationContext(context, fnMapper, varMapper); + ctx.notifyBeforeEvaluation(expr); + + Object value = getNode().getValue(ctx); + + if (expectedType != null) { + try { + value = ctx.convertToType(value, expectedType); + } catch (IllegalArgumentException ex) { + throw new ELException(ex); + } + } + ctx.notifyAfterEvaluation(expr); + return value; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return getNode().hashCode(); + } + + /* + * (non-Javadoc) + * + * @see javax.el.ValueExpression#isLiteralText() + */ + @Override + public boolean isLiteralText() { + try { + return getNode() instanceof AstLiteralExpression; + } catch (ELException ele) { + return false; + } + } + + /* + * (non-Javadoc) + * + * @see javax.el.ValueExpression#isReadOnly(javax.el.ELContext) + */ + @Override + public boolean isReadOnly(ELContext context) throws PropertyNotFoundException, ELException { + return getNode().isReadOnly(new EvaluationContext(context, fnMapper, varMapper)); + } + + @Override + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + expr = in.readUTF(); + String type = in.readUTF(); + if (!"".equals(type)) { + expectedType = forName(type); + } + fnMapper = (FunctionMapper) in.readObject(); + varMapper = (VariableMapper) in.readObject(); + } + + /* + * (non-Javadoc) + * + * @see javax.el.ValueExpression#setValue(javax.el.ELContext, java.lang.Object) + */ + @Override + public void setValue(ELContext context, Object value) throws PropertyNotFoundException, PropertyNotWritableException, ELException { + getNode().setValue(new EvaluationContext(context, fnMapper, varMapper), value); + } + + @Override + public void writeExternal(ObjectOutput out) throws IOException { + out.writeUTF(expr); + out.writeUTF(expectedType != null ? expectedType.getName() : ""); + out.writeObject(fnMapper); + out.writeObject(varMapper); + } + + @Override + public String toString() { + return "ValueExpression[" + expr + "]"; + } +}
diff --git a/impl/src/main/java/com/sun/el/ValueExpressionLiteral.java b/impl/src/main/java/com/sun/el/ValueExpressionLiteral.java new file mode 100644 index 0000000..7dc21e9 --- /dev/null +++ b/impl/src/main/java/com/sun/el/ValueExpressionLiteral.java
@@ -0,0 +1,120 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el; + +import static com.sun.el.util.ReflectionUtil.forName; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +import javax.el.ELContext; +import javax.el.ELException; +import javax.el.PropertyNotWritableException; +import javax.el.ValueExpression; + +import com.sun.el.util.MessageFactory; + +public final class ValueExpressionLiteral extends ValueExpression implements Externalizable { + + private static final long serialVersionUID = 1L; + + private Object value; + private Class<?> expectedType; + + public ValueExpressionLiteral() { + super(); + } + + public ValueExpressionLiteral(Object value, Class<?> expectedType) { + this.value = value; + this.expectedType = expectedType; + } + + @Override + public Object getValue(ELContext context) { + if (expectedType != null) { + try { + return context.convertToType(value, expectedType); + } catch (IllegalArgumentException ex) { + throw new ELException(ex); + } + } + + return value; + } + + @Override + public void setValue(ELContext context, Object value) { + throw new PropertyNotWritableException(MessageFactory.get("error.value.literal.write", value)); + } + + @Override + public boolean isReadOnly(ELContext context) { + return true; + } + + @Override + public Class<?> getType(ELContext context) { + return value != null ? value.getClass() : null; + } + + @Override + public Class<?> getExpectedType() { + return expectedType; + } + + @Override + public String getExpressionString() { + return value != null ? value.toString() : null; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof ValueExpressionLiteral && this.equals((ValueExpressionLiteral) obj); + } + + public boolean equals(ValueExpressionLiteral ve) { + return (ve != null && (this.value != null && ve.value != null && (this.value == ve.value || this.value.equals(ve.value)))); + } + + @Override + public int hashCode() { + return value != null ? value.hashCode() : 0; + } + + @Override + public boolean isLiteralText() { + return true; + } + + @Override + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(value); + out.writeUTF(expectedType != null ? expectedType.getName() : ""); + } + + @Override + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + value = in.readObject(); + String type = in.readUTF(); + if (!"".equals(type)) { + expectedType = forName(type); + } + } +}
diff --git a/impl/src/main/java/com/sun/el/lang/ELArithmetic.java b/impl/src/main/java/com/sun/el/lang/ELArithmetic.java new file mode 100644 index 0000000..37092d4 --- /dev/null +++ b/impl/src/main/java/com/sun/el/lang/ELArithmetic.java
@@ -0,0 +1,423 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.lang; + +import static java.math.BigDecimal.ROUND_HALF_UP; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import com.sun.el.util.MessageFactory; + +/** + * A helper class of Arithmetic defined by the Jakarta Expression Specification + * + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public abstract class ELArithmetic { + + public final static class BigDecimalDelegate extends ELArithmetic { + + @Override + protected Number add(Number num0, Number num1) { + return ((BigDecimal) num0).add((BigDecimal) num1); + } + + @Override + protected Number coerce(Number num) { + if (num instanceof BigDecimal) { + return num; + } + if (num instanceof BigInteger) { + return new BigDecimal((BigInteger) num); + } + + return new BigDecimal(num.doubleValue()); + } + + @Override + protected Number coerce(String str) { + return new BigDecimal(str); + } + + @Override + protected Number divide(Number num0, Number num1) { + return ((BigDecimal) num0).divide((BigDecimal) num1, ROUND_HALF_UP); + } + + @Override + protected Number subtract(Number num0, Number num1) { + return ((BigDecimal) num0).subtract((BigDecimal) num1); + } + + @Override + protected Number mod(Number num0, Number num1) { + return Double.valueOf(num0.doubleValue() % num1.doubleValue()); + } + + @Override + protected Number multiply(Number num0, Number num1) { + return ((BigDecimal) num0).multiply((BigDecimal) num1); + } + + @Override + public boolean matches(Object obj0, Object obj1) { + return (obj0 instanceof BigDecimal || obj1 instanceof BigDecimal); + } + } + + public final static class BigIntegerDelegate extends ELArithmetic { + + @Override + protected Number add(Number num0, Number num1) { + return ((BigInteger) num0).add((BigInteger) num1); + } + + @Override + protected Number coerce(Number num) { + if (num instanceof BigInteger) { + return num; + } + + return new BigInteger(num.toString()); + } + + @Override + protected Number coerce(String str) { + return new BigInteger(str); + } + + @Override + protected Number divide(Number num0, Number num1) { + return (new BigDecimal((BigInteger) num0)).divide(new BigDecimal((BigInteger) num1), ROUND_HALF_UP); + } + + @Override + protected Number multiply(Number num0, Number num1) { + return ((BigInteger) num0).multiply((BigInteger) num1); + } + + @Override + protected Number mod(Number num0, Number num1) { + return ((BigInteger) num0).mod((BigInteger) num1); + } + + @Override + protected Number subtract(Number num0, Number num1) { + return ((BigInteger) num0).subtract((BigInteger) num1); + } + + @Override + public boolean matches(Object obj0, Object obj1) { + return obj0 instanceof BigInteger || obj1 instanceof BigInteger; + } + } + + public final static class DoubleDelegate extends ELArithmetic { + + @Override + protected Number add(Number num0, Number num1) { + // could only be one of these + if (num0 instanceof BigDecimal) { + return ((BigDecimal) num0).add(new BigDecimal(num1.doubleValue())); + } + if (num1 instanceof BigDecimal) { + return ((new BigDecimal(num0.doubleValue()).add((BigDecimal) num1))); + } + + return Double.valueOf(num0.doubleValue() + num1.doubleValue()); + } + + @Override + protected Number coerce(Number num) { + if (num instanceof Double) { + return num; + } + if (num instanceof BigInteger) { + return new BigDecimal((BigInteger) num); + } + + return Double.valueOf(num.doubleValue()); + } + + @Override + protected Number coerce(String str) { + return Double.valueOf(str); + } + + @Override + protected Number divide(Number num0, Number num1) { + return Double.valueOf(num0.doubleValue() / num1.doubleValue()); + } + + @Override + protected Number mod(Number num0, Number num1) { + return Double.valueOf(num0.doubleValue() % num1.doubleValue()); + } + + @Override + protected Number subtract(Number num0, Number num1) { + // could only be one of these + if (num0 instanceof BigDecimal) { + return ((BigDecimal) num0).subtract(new BigDecimal(num1.doubleValue())); + } + + if (num1 instanceof BigDecimal) { + return ((new BigDecimal(num0.doubleValue()).subtract((BigDecimal) num1))); + } + + return Double.valueOf(num0.doubleValue() - num1.doubleValue()); + } + + @Override + protected Number multiply(Number num0, Number num1) { + // could only be one of these + if (num0 instanceof BigDecimal) { + return ((BigDecimal) num0).multiply(new BigDecimal(num1.doubleValue())); + } + if (num1 instanceof BigDecimal) { + return ((new BigDecimal(num0.doubleValue()).multiply((BigDecimal) num1))); + } + + return Double.valueOf(num0.doubleValue() * num1.doubleValue()); + } + + @Override + public boolean matches(Object obj0, Object obj1) { + return (obj0 instanceof Double || obj1 instanceof Double || obj0 instanceof Float || obj1 instanceof Float + || (obj0 != null && (Double.TYPE == obj0.getClass() || Float.TYPE == obj0.getClass())) + || (obj1 != null && (Double.TYPE == obj1.getClass() || Float.TYPE == obj1.getClass())) + || (obj0 instanceof String && ELSupport.isStringFloat((String) obj0)) + || (obj1 instanceof String && ELSupport.isStringFloat((String) obj1))); + } + } + + public final static class LongDelegate extends ELArithmetic { + + @Override + protected Number add(Number num0, Number num1) { + return Long.valueOf(num0.longValue() + num1.longValue()); + } + + @Override + protected Number coerce(Number num) { + if (num instanceof Long) { + return num; + } + + return Long.valueOf(num.longValue()); + } + + @Override + protected Number coerce(String str) { + return Long.valueOf(str); + } + + @Override + protected Number divide(Number num0, Number num1) { + return Long.valueOf(num0.longValue() / num1.longValue()); + } + + @Override + protected Number mod(Number num0, Number num1) { + return Long.valueOf(num0.longValue() % num1.longValue()); + } + + @Override + protected Number subtract(Number num0, Number num1) { + return Long.valueOf(num0.longValue() - num1.longValue()); + } + + @Override + protected Number multiply(Number num0, Number num1) { + return Long.valueOf(num0.longValue() * num1.longValue()); + } + + @Override + public boolean matches(Object obj0, Object obj1) { + return obj0 instanceof Long || obj1 instanceof Long; + } + } + + public static final BigDecimalDelegate BIGDECIMAL = new BigDecimalDelegate(); + public static final BigIntegerDelegate BIGINTEGER = new BigIntegerDelegate(); + public static final DoubleDelegate DOUBLE = new DoubleDelegate(); + public static final LongDelegate LONG = new LongDelegate(); + private static final Long ZERO = Long.valueOf(0); + + public final static Number add(final Object obj0, final Object obj1) { + if (obj0 == null && obj1 == null) { + return Long.valueOf(0); + } + + final ELArithmetic delegate; + if (BIGDECIMAL.matches(obj0, obj1)) { + delegate = BIGDECIMAL; + } else if (DOUBLE.matches(obj0, obj1)) { + delegate = DOUBLE; + } else if (BIGINTEGER.matches(obj0, obj1)) { + delegate = BIGINTEGER; + } else { + delegate = LONG; + } + + Number num0 = delegate.coerce(obj0); + Number num1 = delegate.coerce(obj1); + + return delegate.add(num0, num1); + } + + public final static Number mod(final Object obj0, final Object obj1) { + if (obj0 == null && obj1 == null) { + return Long.valueOf(0); + } + + final ELArithmetic delegate; + if (BIGDECIMAL.matches(obj0, obj1)) { + delegate = BIGDECIMAL; + } else if (DOUBLE.matches(obj0, obj1)) { + delegate = DOUBLE; + } else if (BIGINTEGER.matches(obj0, obj1)) { + delegate = BIGINTEGER; + } else { + delegate = LONG; + } + + Number num0 = delegate.coerce(obj0); + Number num1 = delegate.coerce(obj1); + + return delegate.mod(num0, num1); + } + + public final static Number subtract(final Object obj0, final Object obj1) { + if (obj0 == null && obj1 == null) { + return Long.valueOf(0); + } + + final ELArithmetic delegate; + if (BIGDECIMAL.matches(obj0, obj1)) { + delegate = BIGDECIMAL; + } else if (DOUBLE.matches(obj0, obj1)) { + delegate = DOUBLE; + } else if (BIGINTEGER.matches(obj0, obj1)) { + delegate = BIGINTEGER; + } else { + delegate = LONG; + } + + Number num0 = delegate.coerce(obj0); + Number num1 = delegate.coerce(obj1); + + return delegate.subtract(num0, num1); + } + + public final static Number divide(final Object obj0, final Object obj1) { + if (obj0 == null && obj1 == null) { + return ZERO; + } + + final ELArithmetic delegate; + if (BIGDECIMAL.matches(obj0, obj1)) { + delegate = BIGDECIMAL; + } else if (BIGINTEGER.matches(obj0, obj1)) { + delegate = BIGDECIMAL; + } else { + delegate = DOUBLE; + } + + Number num0 = delegate.coerce(obj0); + Number num1 = delegate.coerce(obj1); + + return delegate.divide(num0, num1); + } + + public final static Number multiply(final Object obj0, final Object obj1) { + if (obj0 == null && obj1 == null) { + return Long.valueOf(0); + } + + final ELArithmetic delegate; + if (BIGDECIMAL.matches(obj0, obj1)) { + delegate = BIGDECIMAL; + } else if (DOUBLE.matches(obj0, obj1)) { + delegate = DOUBLE; + } else if (BIGINTEGER.matches(obj0, obj1)) { + delegate = BIGINTEGER; + } else { + delegate = LONG; + } + + Number num0 = delegate.coerce(obj0); + Number num1 = delegate.coerce(obj1); + + return delegate.multiply(num0, num1); + } + + public final static boolean isNumber(final Object obj) { + return (obj != null && isNumberType(obj.getClass())); + } + + public final static boolean isNumberType(final Class type) { + return type == Long.TYPE || type == Double.TYPE || type == Byte.TYPE || type == Short.TYPE || type == Integer.TYPE || type == Float.TYPE + || Number.class.isAssignableFrom(type); + } + + /** + * + */ + protected ELArithmetic() { + super(); + } + + protected abstract Number add(final Number num0, final Number num1); + + protected abstract Number multiply(final Number num0, final Number num1); + + protected abstract Number subtract(final Number num0, final Number num1); + + protected abstract Number mod(final Number num0, final Number num1); + + protected abstract Number coerce(final Number num); + + protected final Number coerce(final Object obj) { + + if (isNumber(obj)) { + return coerce((Number) obj); + } + if (obj instanceof String) { + return coerce((String) obj); + } + if (obj == null || "".equals(obj)) { + return coerce(ZERO); + } + + Class objType = obj.getClass(); + if (Character.class.equals(objType) || Character.TYPE == objType) { + return coerce(Short.valueOf((short) ((Character) obj).charValue())); + } + + throw new IllegalArgumentException(MessageFactory.get("el.convert", obj, objType)); + } + + protected abstract Number coerce(final String str); + + protected abstract Number divide(final Number num0, final Number num1); + + protected abstract boolean matches(final Object obj0, final Object obj1); + +}
diff --git a/impl/src/main/java/com/sun/el/lang/ELSupport.java b/impl/src/main/java/com/sun/el/lang/ELSupport.java new file mode 100644 index 0000000..f0a8368 --- /dev/null +++ b/impl/src/main/java/com/sun/el/lang/ELSupport.java
@@ -0,0 +1,467 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.lang; + +import java.beans.PropertyEditor; +import java.beans.PropertyEditorManager; +import java.math.BigDecimal; +import java.math.BigInteger; + +import javax.el.ELException; +import javax.el.PropertyNotFoundException; + +import com.sun.el.util.MessageFactory; + +/** + * A helper class that implements the Jakarta Expression Specification + * + * @author Jacob Hookom [jacob@hookom.net] + * @author Kin-man Chung + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public class ELSupport { + + private final static Long ZERO = Long.valueOf(0L); + + public final static void throwUnhandled(Object base, Object property) throws ELException { + if (base == null) { + throw new PropertyNotFoundException(MessageFactory.get("error.resolver.unhandled.null", property)); + } else { + throw new PropertyNotFoundException(MessageFactory.get("error.resolver.unhandled", base.getClass(), property)); + } + } + + /** + * @param obj0 First object to be compared + * @param obj1 Second object to be compared + * @return The result (an int with values -1, 0, or 1) of the comparison + * @throws ELException when something goes wrong + */ + public final static int compare(final Object obj0, final Object obj1) throws ELException { + if (obj0 == obj1 || equals(obj0, obj1)) { + return 0; + } + if (isBigDecimalOp(obj0, obj1)) { + BigDecimal bd0 = (BigDecimal) coerceToNumber(obj0, BigDecimal.class); + BigDecimal bd1 = (BigDecimal) coerceToNumber(obj1, BigDecimal.class); + return bd0.compareTo(bd1); + } + if (isDoubleOp(obj0, obj1)) { + Double d0 = (Double) coerceToNumber(obj0, Double.class); + Double d1 = (Double) coerceToNumber(obj1, Double.class); + return d0.compareTo(d1); + } + if (isBigIntegerOp(obj0, obj1)) { + BigInteger bi0 = (BigInteger) coerceToNumber(obj0, BigInteger.class); + BigInteger bi1 = (BigInteger) coerceToNumber(obj1, BigInteger.class); + return bi0.compareTo(bi1); + } + if (isLongOp(obj0, obj1)) { + Long l0 = (Long) coerceToNumber(obj0, Long.class); + Long l1 = (Long) coerceToNumber(obj1, Long.class); + return l0.compareTo(l1); + } + if (obj0 instanceof String || obj1 instanceof String) { + return coerceToString(obj0).compareTo(coerceToString(obj1)); + } + if (obj0 instanceof Comparable) { + // Safe cast + @SuppressWarnings("unchecked") + Comparable<Object> cobj0 = (Comparable) obj0; + return (obj1 != null) ? cobj0.compareTo(obj1) : 1; + } + if (obj1 instanceof Comparable) { + // Safe cast + @SuppressWarnings("unchecked") + Comparable<Object> cobj1 = (Comparable) obj1; + return (obj0 != null) ? -(cobj1.compareTo(obj0)) : -1; + } + throw new ELException(MessageFactory.get("error.compare", obj0, obj1)); + } + + /** + * @param obj0 Fisrt object to be compared + * @param obj1 Second object to be compared + * @return true if the objects compared equal + * @throws ELException when something goes wrong + */ + public final static boolean equals(final Object obj0, final Object obj1) throws ELException { + if (obj0 == obj1) { + return true; + } + if (obj0 == null || obj1 == null) { + return false; + } + if (obj0 instanceof Boolean || obj1 instanceof Boolean) { + return coerceToBoolean(obj0).equals(coerceToBoolean(obj1)); + } + if (obj0.getClass().isEnum()) { + return obj0.equals(coerceToEnum(obj1, obj0.getClass())); + } + if (obj1.getClass().isEnum()) { + return obj1.equals(coerceToEnum(obj0, obj1.getClass())); + } + if (obj0 instanceof String || obj1 instanceof String) { + return coerceToString(obj0).equals(coerceToString(obj1)); + } + if (isBigDecimalOp(obj0, obj1)) { + BigDecimal bd0 = (BigDecimal) coerceToNumber(obj0, BigDecimal.class); + BigDecimal bd1 = (BigDecimal) coerceToNumber(obj1, BigDecimal.class); + return bd0.equals(bd1); + } + if (isDoubleOp(obj0, obj1)) { + Double d0 = (Double) coerceToNumber(obj0, Double.class); + Double d1 = (Double) coerceToNumber(obj1, Double.class); + return d0.equals(d1); + } + if (isBigIntegerOp(obj0, obj1)) { + BigInteger bi0 = (BigInteger) coerceToNumber(obj0, BigInteger.class); + BigInteger bi1 = (BigInteger) coerceToNumber(obj1, BigInteger.class); + return bi0.equals(bi1); + } + if (isLongOp(obj0, obj1)) { + Long l0 = (Long) coerceToNumber(obj0, Long.class); + Long l1 = (Long) coerceToNumber(obj1, Long.class); + return l0.equals(l1); + } else { + return obj0.equals(obj1); + } + } + + /** + * @param obj Object to be coerced + * @return The result of coercion + */ + public final static Boolean coerceToBoolean(final Object obj) throws IllegalArgumentException { + if (obj == null || "".equals(obj)) { + return Boolean.FALSE; + } + if (obj instanceof Boolean) { + return (Boolean) obj; + } + if (obj instanceof String) { + return Boolean.valueOf((String) obj); + } + + throw new IllegalArgumentException(MessageFactory.get("error.convert", obj, obj.getClass(), Boolean.class)); + } + + // Enum types are hard construct. We can declare this as + // <T extends Enum<T>> T coerceToEnum(Object, Class<T> type) + // but this makes it harder to get the calls right. + @SuppressWarnings("unchecked") + public final static Enum coerceToEnum(final Object obj, Class type) { + if (obj == null || "".equals(obj)) { + return null; + } + if (obj.getClass().isEnum()) { + return (Enum) obj; + } + return Enum.valueOf(type, obj.toString()); + } + + public final static Character coerceToCharacter(final Object obj) throws IllegalArgumentException { + if (obj == null || "".equals(obj)) { + return Character.valueOf((char) 0); + } + if (obj instanceof String) { + return Character.valueOf(((String) obj).charAt(0)); + } + if (ELArithmetic.isNumber(obj)) { + return Character.valueOf((char) ((Number) obj).shortValue()); + } + Class objType = obj.getClass(); + if (obj instanceof Character) { + return (Character) obj; + } + + throw new IllegalArgumentException(MessageFactory.get("error.convert", obj, objType, Character.class)); + } + + public final static Number coerceToNumber(final Object obj) { + if (obj == null) { + return ZERO; + } else if (obj instanceof Number) { + return (Number) obj; + } else { + String str = coerceToString(obj); + if (isStringFloat(str)) { + return toFloat(str); + } else { + return toNumber(str); + } + } + } + + protected final static Number coerceToNumber(final Number number, final Class type) throws IllegalArgumentException { + if (Long.TYPE == type || Long.class.equals(type)) { + return Long.valueOf(number.longValue()); + } + if (Double.TYPE == type || Double.class.equals(type)) { + return Double.valueOf(number.doubleValue()); + } + if (Integer.TYPE == type || Integer.class.equals(type)) { + return Integer.valueOf(number.intValue()); + } + if (BigInteger.class.equals(type)) { + if (number instanceof BigDecimal) { + return ((BigDecimal) number).toBigInteger(); + } + if (number instanceof BigInteger) { + return number; + } + return BigInteger.valueOf(number.longValue()); + } + if (BigDecimal.class.equals(type)) { + if (number instanceof BigDecimal) { + return number; + } + if (number instanceof BigInteger) { + return new BigDecimal((BigInteger) number); + } + if (number instanceof Long) { + return new BigDecimal((Long) number); + } + return new BigDecimal(number.doubleValue()); + } + if (Byte.TYPE == type || Byte.class.equals(type)) { + return Byte.valueOf(number.byteValue()); + } + if (Short.TYPE == type || Short.class.equals(type)) { + return Short.valueOf(number.shortValue()); + } + if (Float.TYPE == type || Float.class.equals(type)) { + return Float.valueOf(number.floatValue()); + } + + throw new IllegalArgumentException(MessageFactory.get("error.convert", number, number.getClass(), type)); + } + + public final static Number coerceToNumber(final Object obj, final Class type) throws IllegalArgumentException { + if (obj == null || "".equals(obj)) { + return coerceToNumber(ZERO, type); + } + if (obj instanceof String) { + return coerceToNumber((String) obj, type); + } + if (ELArithmetic.isNumber(obj)) { + if (obj.getClass().equals(type)) { + return (Number) obj; + } + return coerceToNumber((Number) obj, type); + } + + if (obj instanceof Character) { + return coerceToNumber(Short.valueOf((short) ((Character) obj).charValue()), type); + } + + throw new IllegalArgumentException(MessageFactory.get("error.convert", obj, obj.getClass(), type)); + } + + protected final static Number coerceToNumber(final String val, final Class type) throws IllegalArgumentException { + if (Long.TYPE == type || Long.class.equals(type)) { + return Long.valueOf(val); + } + if (Integer.TYPE == type || Integer.class.equals(type)) { + return Integer.valueOf(val); + } + if (Double.TYPE == type || Double.class.equals(type)) { + return Double.valueOf(val); + } + if (BigInteger.class.equals(type)) { + return new BigInteger(val); + } + if (BigDecimal.class.equals(type)) { + return new BigDecimal(val); + } + if (Byte.TYPE == type || Byte.class.equals(type)) { + return Byte.valueOf(val); + } + if (Short.TYPE == type || Short.class.equals(type)) { + return Short.valueOf(val); + } + if (Float.TYPE == type || Float.class.equals(type)) { + return Float.valueOf(val); + } + + throw new IllegalArgumentException(MessageFactory.get("error.convert", val, String.class, type)); + } + + /** + * @param obj Object to be coerced + * @return The result of coercion + */ + public final static String coerceToString(final Object obj) { + if (obj == null) { + return ""; + } else if (obj instanceof String) { + return (String) obj; + } else if (obj instanceof Enum) { + return ((Enum) obj).name(); + } else { + return obj.toString(); + } + } + + public final static void checkType(final Object obj, final Class<?> type) throws IllegalArgumentException { + if (String.class.equals(type)) { + coerceToString(obj); + } + if (ELArithmetic.isNumberType(type)) { + coerceToNumber(obj, type); + } + if (Character.class.equals(type) || Character.TYPE == type) { + coerceToCharacter(obj); + } + if (Boolean.class.equals(type) || Boolean.TYPE == type) { + coerceToBoolean(obj); + } + if (type.isEnum()) { + coerceToEnum(obj, type); + } + } + + public final static Object coerceToType(final Object obj, final Class<?> type) throws IllegalArgumentException { + return coerceToType(obj, type, false); + } + + public final static Object coerceToType(final Object obj, final Class<?> type, boolean isEL22Compatible) throws IllegalArgumentException { + + if (type == null || Object.class.equals(type) || (obj != null && type.isAssignableFrom(obj.getClass()))) { + return obj; + } + + // New in 3.0 + if (!isEL22Compatible && obj == null && !type.isPrimitive() && !String.class.equals(type)) { + return null; + } + + if (String.class.equals(type)) { + return coerceToString(obj); + } + if (ELArithmetic.isNumberType(type)) { + return coerceToNumber(obj, type); + } + if (Character.class.equals(type) || Character.TYPE == type) { + return coerceToCharacter(obj); + } + if (Boolean.class.equals(type) || Boolean.TYPE == type) { + return coerceToBoolean(obj); + } + if (type.isEnum()) { + return coerceToEnum(obj, type); + } + + if (obj == null) { + return null; + } + + if (obj instanceof String) { + if ("".equals(obj)) { + return null; + } + PropertyEditor editor = PropertyEditorManager.findEditor(type); + if (editor != null) { + editor.setAsText((String) obj); + return editor.getValue(); + } + } + throw new IllegalArgumentException(MessageFactory.get("error.convert", obj, obj.getClass(), type)); + } + + /** + * @param obj An array of objects + * @return true if the array contains a null, false otherwise + */ + public final static boolean containsNulls(final Object[] obj) { + for (int i = 0; i < obj.length; i++) { + if (obj[0] == null) { + return true; + } + } + return false; + } + + public final static boolean isBigDecimalOp(final Object obj0, final Object obj1) { + return (obj0 instanceof BigDecimal || obj1 instanceof BigDecimal); + } + + public final static boolean isBigIntegerOp(final Object obj0, final Object obj1) { + return (obj0 instanceof BigInteger || obj1 instanceof BigInteger); + } + + public final static boolean isDoubleOp(final Object obj0, final Object obj1) { + return (obj0 instanceof Double || obj1 instanceof Double || obj0 instanceof Float || obj1 instanceof Float); + } + + public final static boolean isDoubleStringOp(final Object obj0, final Object obj1) { + return (isDoubleOp(obj0, obj1) || (obj0 instanceof String && isStringFloat((String) obj0)) || (obj1 instanceof String && isStringFloat((String) obj1))); + } + + public final static boolean isLongOp(final Object obj0, final Object obj1) { + return (obj0 instanceof Long || obj1 instanceof Long || obj0 instanceof Integer || obj1 instanceof Integer || obj0 instanceof Character + || obj1 instanceof Character || obj0 instanceof Short || obj1 instanceof Short || obj0 instanceof Byte || obj1 instanceof Byte); + } + + public final static boolean isStringFloat(final String str) { + int len = str.length(); + if (len > 1) { + for (int i = 0; i < len; i++) { + switch (str.charAt(i)) { + case 'E': + return true; + case 'e': + return true; + case '.': + return true; + } + } + } + return false; + } + + public final static Number toFloat(final String value) { + try { + if (Double.parseDouble(value) > Double.MAX_VALUE) { + return new BigDecimal(value); + } else { + return Double.valueOf(value); + } + } catch (NumberFormatException e0) { + return new BigDecimal(value); + } + } + + public final static Number toNumber(final String value) { + try { + return Integer.valueOf(Integer.parseInt(value)); + } catch (NumberFormatException e0) { + try { + return Long.valueOf(Long.parseLong(value)); + } catch (NumberFormatException e1) { + return new BigInteger(value); + } + } + } + + /** + * + */ + public ELSupport() { + super(); + } +}
diff --git a/impl/src/main/java/com/sun/el/lang/EvaluationContext.java b/impl/src/main/java/com/sun/el/lang/EvaluationContext.java new file mode 100644 index 0000000..4b07a60 --- /dev/null +++ b/impl/src/main/java/com/sun/el/lang/EvaluationContext.java
@@ -0,0 +1,144 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.lang; + +import java.util.List; +import java.util.Map; + +import javax.el.ELContext; +import javax.el.ELResolver; +import javax.el.EvaluationListener; +import javax.el.FunctionMapper; +import javax.el.ImportHandler; +import javax.el.VariableMapper; + +/** + * The context for Jakarta Expression expression evaluation. This wrapper ELContext captures the function mapper and the + * variable mapper at the point when the expression is parsed, and only for those functions and variable used in the + * expression. + */ +public final class EvaluationContext extends ELContext { + + private final ELContext elContext; + private final FunctionMapper fnMapper; + private final VariableMapper varMapper; + + public EvaluationContext(ELContext elContext, FunctionMapper fnMapper, VariableMapper varMapper) { + this.elContext = elContext; + this.fnMapper = fnMapper; + this.varMapper = varMapper; + } + + public ELContext getELContext() { + return elContext; + } + + @Override + public FunctionMapper getFunctionMapper() { + return fnMapper; + } + + @Override + public VariableMapper getVariableMapper() { + return varMapper; + } + + @Override + public Object getContext(Class key) { + return elContext.getContext(key); + } + + @Override + public ELResolver getELResolver() { + return elContext.getELResolver(); + } + + @Override + public boolean isPropertyResolved() { + return elContext.isPropertyResolved(); + } + + @Override + public void putContext(Class key, Object contextObject) { + elContext.putContext(key, contextObject); + } + + @Override + public void setPropertyResolved(boolean resolved) { + elContext.setPropertyResolved(resolved); + } + + @Override + public void setPropertyResolved(Object base, Object property) { + elContext.setPropertyResolved(base, property); + } + + @Override + public void addEvaluationListener(EvaluationListener listener) { + elContext.addEvaluationListener(listener); + } + + @Override + public List<EvaluationListener> getEvaluationListeners() { + return elContext.getEvaluationListeners(); + } + + @Override + public void notifyBeforeEvaluation(String expr) { + elContext.notifyBeforeEvaluation(expr); + } + + @Override + public void notifyAfterEvaluation(String expr) { + elContext.notifyAfterEvaluation(expr); + } + + @Override + public void notifyPropertyResolved(Object base, Object property) { + elContext.notifyPropertyResolved(base, property); + } + + @Override + public boolean isLambdaArgument(String arg) { + return elContext.isLambdaArgument(arg); + } + + @Override + public Object getLambdaArgument(String arg) { + return elContext.getLambdaArgument(arg); + } + + @Override + public void enterLambdaScope(Map<String, Object> args) { + elContext.enterLambdaScope(args); + } + + @Override + public void exitLambdaScope() { + elContext.exitLambdaScope(); + } + + @Override + public Object convertToType(Object obj, Class<?> targetType) { + return elContext.convertToType(obj, targetType); + } + + @Override + public ImportHandler getImportHandler() { + return elContext.getImportHandler(); + } +}
diff --git a/impl/src/main/java/com/sun/el/lang/ExpressionBuilder.java b/impl/src/main/java/com/sun/el/lang/ExpressionBuilder.java new file mode 100644 index 0000000..e1de322 --- /dev/null +++ b/impl/src/main/java/com/sun/el/lang/ExpressionBuilder.java
@@ -0,0 +1,263 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.lang; + +import java.io.StringReader; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.lang.reflect.Method; +import java.util.concurrent.ConcurrentHashMap; + +import javax.el.ELContext; +import javax.el.ELException; +import javax.el.FunctionMapper; +import javax.el.MethodExpression; +import javax.el.ValueExpression; +import javax.el.VariableMapper; + +import com.sun.el.MethodExpressionImpl; +import com.sun.el.MethodExpressionLiteral; +import com.sun.el.ValueExpressionImpl; +import com.sun.el.parser.AstCompositeExpression; +import com.sun.el.parser.AstDeferredExpression; +import com.sun.el.parser.AstDynamicExpression; +import com.sun.el.parser.AstFunction; +import com.sun.el.parser.AstIdentifier; +import com.sun.el.parser.AstLiteralExpression; +import com.sun.el.parser.AstMethodArguments; +import com.sun.el.parser.AstValue; +import com.sun.el.parser.ELParser; +import com.sun.el.parser.Node; +import com.sun.el.parser.NodeVisitor; +import com.sun.el.parser.ParseException; +import com.sun.el.util.MessageFactory; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @author Kin-man Chung // EL cache + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class ExpressionBuilder implements NodeVisitor { + + static private class NodeSoftReference extends SoftReference<Node> { + final String key; + + NodeSoftReference(String key, Node node, ReferenceQueue<Node> refQ) { + super(node, refQ); + this.key = key; + } + } + + static private class SoftConcurrentHashMap extends ConcurrentHashMap<String, Node> { + + private static final int CACHE_INIT_SIZE = 256; + private ConcurrentHashMap<String, NodeSoftReference> map = new ConcurrentHashMap<String, NodeSoftReference>(CACHE_INIT_SIZE); + private ReferenceQueue<Node> refQ = new ReferenceQueue<Node>(); + + // Remove map entries that have been placed on the queue by GC. + private void cleanup() { + NodeSoftReference nodeRef = null; + while ((nodeRef = (NodeSoftReference) refQ.poll()) != null) { + map.remove(nodeRef.key); + } + } + + @Override + public Node put(String key, Node value) { + cleanup(); + NodeSoftReference prev = map.put(key, new NodeSoftReference(key, value, refQ)); + return prev == null ? null : prev.get(); + } + + @Override + public Node putIfAbsent(String key, Node value) { + cleanup(); + NodeSoftReference prev = map.putIfAbsent(key, new NodeSoftReference(key, value, refQ)); + return prev == null ? null : prev.get(); + } + + @Override + public Node get(Object key) { + cleanup(); + NodeSoftReference nodeRef = map.get(key); + if (nodeRef == null) { + return null; + } + if (nodeRef.get() == null) { + // value has been garbage collected, remove entry in map + map.remove(key); + return null; + } + return nodeRef.get(); + } + } + + private static final SoftConcurrentHashMap cache = new SoftConcurrentHashMap(); + private FunctionMapper fnMapper; + private VariableMapper varMapper; + private String expression; + + /** + * + */ + public ExpressionBuilder(String expression, ELContext ctx) throws ELException { + this.expression = expression; + + FunctionMapper ctxFn = ctx.getFunctionMapper(); + VariableMapper ctxVar = ctx.getVariableMapper(); + + if (ctxFn != null) { + this.fnMapper = new FunctionMapperFactory(ctxFn); + } + if (ctxVar != null) { + this.varMapper = new VariableMapperFactory(ctxVar); + } + } + + public static Node createNode(String expr) throws ELException { + Node n = createNodeInternal(expr); + return n; + } + + private static Node createNodeInternal(String expr) throws ELException { + if (expr == null) { + throw new ELException(MessageFactory.get("error.null")); + } + + Node n = cache.get(expr); + if (n == null) { + try { + n = (new ELParser( + new com.sun.el.parser.ELParserTokenManager(new com.sun.el.parser.SimpleCharStream(new StringReader(expr), 1, 1, expr.length() + 1)))) + .CompositeExpression(); + + // validate composite expression + if (n instanceof AstCompositeExpression) { + int numChildren = n.jjtGetNumChildren(); + if (numChildren == 1) { + n = n.jjtGetChild(0); + } else { + Class type = null; + Node child = null; + for (int i = 0; i < numChildren; i++) { + child = n.jjtGetChild(i); + if (child instanceof AstLiteralExpression) { + continue; + } + if (type == null) { + type = child.getClass(); + } else { + if (!type.equals(child.getClass())) { + throw new ELException(MessageFactory.get("error.mixed", expr)); + } + } + } + } + } + if (n instanceof AstDeferredExpression || n instanceof AstDynamicExpression) { + n = n.jjtGetChild(0); + } + cache.putIfAbsent(expr, n); + } catch (ParseException pe) { + throw new ELException("Error Parsing: " + expr, pe); + } + } + return n; + } + + /** + * Scan the expression nodes and captures the functions and variables used in this expression. This ensures that any + * changes to the functions or variables mappings during the expression will not affect the evaluation of this + * expression, as the functions and variables are bound and resolved at parse time, as specified in the spec. + */ + private void prepare(Node node) throws ELException { + node.accept(this); + if (this.fnMapper instanceof FunctionMapperFactory) { + this.fnMapper = ((FunctionMapperFactory) this.fnMapper).create(); + } + if (this.varMapper instanceof VariableMapperFactory) { + this.varMapper = ((VariableMapperFactory) this.varMapper).create(); + } + } + + private Node build() throws ELException { + Node n = createNodeInternal(this.expression); + this.prepare(n); + if (n instanceof AstDeferredExpression || n instanceof AstDynamicExpression) { + n = n.jjtGetChild(0); + } + return n; + } + + /* + * (non-Javadoc) + * + * @see com.sun.el.parser.NodeVisitor#visit(com.sun.el.parser.Node) + */ + @Override + public void visit(Node node) throws ELException { + if (node instanceof AstFunction) { + AstFunction funcNode = (AstFunction) node; + if ((funcNode.getPrefix().length() == 0) + && (this.fnMapper == null || fnMapper.resolveFunction(funcNode.getPrefix(), funcNode.getLocalName()) == null)) { + // This can be a call to a LambdaExpression. The target + // of the call is a bean or an Jakarta Expression variable. Capture + // the variable name in the variable mapper if it is an + // variable. The decision to invoke the static method or + // the LambdaExpression will be made at runtime. + if (this.varMapper != null) { + this.varMapper.resolveVariable(funcNode.getLocalName()); + } + return; + } + + if (this.fnMapper == null) { + throw new ELException(MessageFactory.get("error.fnMapper.null")); + } + Method m = fnMapper.resolveFunction(funcNode.getPrefix(), funcNode.getLocalName()); + if (m == null) { + throw new ELException(MessageFactory.get("error.fnMapper.method", funcNode.getOutputName())); + } + int pcnt = m.getParameterTypes().length; + int acnt = ((AstMethodArguments) node.jjtGetChild(0)).getParameterCount(); + if (acnt != pcnt) { + throw new ELException(MessageFactory.get("error.fnMapper.paramcount", funcNode.getOutputName(), "" + pcnt, "" + acnt)); + } + } else if (node instanceof AstIdentifier && this.varMapper != null) { + String variable = ((AstIdentifier) node).getImage(); + + // simply capture it + this.varMapper.resolveVariable(variable); + } + } + + public ValueExpression createValueExpression(Class expectedType) throws ELException { + Node n = this.build(); + return new ValueExpressionImpl(this.expression, n, this.fnMapper, this.varMapper, expectedType); + } + + public MethodExpression createMethodExpression(Class expectedReturnType, Class[] expectedParamTypes) throws ELException { + Node n = this.build(); + if (n instanceof AstValue || n instanceof AstIdentifier) { + return new MethodExpressionImpl(expression, n, this.fnMapper, this.varMapper, expectedReturnType, expectedParamTypes); + } else if (n instanceof AstLiteralExpression) { + return new MethodExpressionLiteral(expression, expectedReturnType, expectedParamTypes); + } else { + throw new ELException("Not a Valid Method Expression: " + expression); + } + } +}
diff --git a/impl/src/main/java/com/sun/el/lang/FunctionMapperFactory.java b/impl/src/main/java/com/sun/el/lang/FunctionMapperFactory.java new file mode 100644 index 0000000..3bb0157 --- /dev/null +++ b/impl/src/main/java/com/sun/el/lang/FunctionMapperFactory.java
@@ -0,0 +1,60 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.lang; + +import java.lang.reflect.Method; + +import javax.el.FunctionMapper; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public class FunctionMapperFactory extends FunctionMapper { + + protected FunctionMapperImpl memento = null; + protected FunctionMapper target; + + public FunctionMapperFactory(FunctionMapper mapper) { + if (mapper == null) { + throw new NullPointerException("FunctionMapper target cannot be null"); + } + this.target = mapper; + } + + /* + * (non-Javadoc) + * + * @see javax.el.FunctionMapper#resolveFunction(java.lang.String, java.lang.String) + */ + @Override + public Method resolveFunction(String prefix, String localName) { + if (this.memento == null) { + this.memento = new FunctionMapperImpl(); + } + Method m = this.target.resolveFunction(prefix, localName); + if (m != null) { + this.memento.addFunction(prefix, localName, m); + } + return m; + } + + public FunctionMapper create() { + return this.memento; + } + +}
diff --git a/impl/src/main/java/com/sun/el/lang/FunctionMapperImpl.java b/impl/src/main/java/com/sun/el/lang/FunctionMapperImpl.java new file mode 100644 index 0000000..970bc0e --- /dev/null +++ b/impl/src/main/java/com/sun/el/lang/FunctionMapperImpl.java
@@ -0,0 +1,210 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.lang; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import javax.el.FunctionMapper; + +import com.sun.el.util.ReflectionUtil; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public class FunctionMapperImpl extends FunctionMapper implements Externalizable { + + private static final long serialVersionUID = 1L; + + protected Map<String, Function> functions = null; + + /* + * (non-Javadoc) + * + * @see javax.el.FunctionMapper#resolveFunction(java.lang.String, java.lang.String) + */ + @Override + public Method resolveFunction(String prefix, String localName) { + if (this.functions != null) { + Function f = this.functions.get(prefix + ":" + localName); + return f.getMethod(); + } + return null; + } + + public void addFunction(String prefix, String localName, Method m) { + if (this.functions == null) { + this.functions = new HashMap<String, Function>(); + } + Function f = new Function(prefix, localName, m); + synchronized (this) { + this.functions.put(prefix + ":" + localName, f); + } + } + + /* + * (non-Javadoc) + * + * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput) + */ + @Override + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(this.functions); + } + + /* + * (non-Javadoc) + * + * @see java.io.Externalizable#readExternal(java.io.ObjectInput) + */ + // Safe cast + @Override + @SuppressWarnings("unchecked") + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + this.functions = (Map<String, Function>) in.readObject(); + } + + public static class Function implements Externalizable { + + protected transient Method m; + protected String owner; + protected String name; + protected String[] types; + protected String prefix; + protected String localName; + + /** + * + */ + public Function(String prefix, String localName, Method m) { + if (localName == null) { + throw new NullPointerException("LocalName cannot be null"); + } + if (m == null) { + throw new NullPointerException("Method cannot be null"); + } + this.prefix = prefix; + this.localName = localName; + this.m = m; + } + + public Function() { + // for serialization + } + + /* + * (non-Javadoc) + * + * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput) + */ + @Override + public void writeExternal(ObjectOutput out) throws IOException { + + out.writeUTF((this.prefix != null) ? this.prefix : ""); + out.writeUTF(this.localName); + + if (this.owner != null) { + out.writeUTF(this.owner); + } else { + out.writeUTF(this.m.getDeclaringClass().getName()); + } + if (this.name != null) { + out.writeUTF(this.name); + } else { + out.writeUTF(this.m.getName()); + } + if (this.types != null) { + out.writeObject(this.types); + } else { + out.writeObject(ReflectionUtil.toTypeNameArray(this.m.getParameterTypes())); + } + } + + /* + * (non-Javadoc) + * + * @see java.io.Externalizable#readExternal(java.io.ObjectInput) + */ + @Override + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + + this.prefix = in.readUTF(); + if ("".equals(this.prefix)) { + this.prefix = null; + } + this.localName = in.readUTF(); + this.owner = in.readUTF(); + this.name = in.readUTF(); + this.types = (String[]) in.readObject(); + } + + public Method getMethod() { + if (this.m == null) { + try { + Class<?> t = Class.forName(this.owner, false, Thread.currentThread().getContextClassLoader()); + Class[] p = ReflectionUtil.toTypeArray(this.types); + this.m = t.getMethod(this.name, p); + } catch (Exception e) { + e.printStackTrace(); + } + } + return this.m; + } + + public boolean matches(String prefix, String localName) { + if (this.prefix != null) { + if (prefix == null) { + return false; + } + if (!this.prefix.equals(prefix)) { + return false; + } + } + return this.localName.equals(localName); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof Function) { + return this.hashCode() == obj.hashCode(); + } + return false; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return (this.prefix + this.localName).hashCode(); + } + } + +}
diff --git a/impl/src/main/java/com/sun/el/lang/VariableMapperFactory.java b/impl/src/main/java/com/sun/el/lang/VariableMapperFactory.java new file mode 100644 index 0000000..b67aade --- /dev/null +++ b/impl/src/main/java/com/sun/el/lang/VariableMapperFactory.java
@@ -0,0 +1,57 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.lang; + +import javax.el.ValueExpression; +import javax.el.VariableMapper; + +/** + * Creates a VariableMapper for the variables used in the expression. + */ +public class VariableMapperFactory extends VariableMapper { + + private final VariableMapper target; + private VariableMapper momento; + + public VariableMapperFactory(VariableMapper target) { + if (target == null) { + throw new NullPointerException("Target VariableMapper cannot be null"); + } + this.target = target; + } + + public VariableMapper create() { + return this.momento; + } + + @Override + public ValueExpression resolveVariable(String variable) { + ValueExpression expr = this.target.resolveVariable(variable); + if (expr != null) { + if (this.momento == null) { + this.momento = new VariableMapperImpl(); + } + this.momento.setVariable(variable, expr); + } + return expr; + } + + @Override + public ValueExpression setVariable(String variable, ValueExpression expression) { + throw new UnsupportedOperationException("Cannot Set Variables on Factory"); + } +}
diff --git a/impl/src/main/java/com/sun/el/lang/VariableMapperImpl.java b/impl/src/main/java/com/sun/el/lang/VariableMapperImpl.java new file mode 100644 index 0000000..027bbe6 --- /dev/null +++ b/impl/src/main/java/com/sun/el/lang/VariableMapperImpl.java
@@ -0,0 +1,60 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.lang; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.util.HashMap; +import java.util.Map; + +import javax.el.ValueExpression; +import javax.el.VariableMapper; + +public class VariableMapperImpl extends VariableMapper implements Externalizable { + + private static final long serialVersionUID = 1L; + + private Map<String, ValueExpression> vars = new HashMap<String, ValueExpression>(); + + public VariableMapperImpl() { + super(); + } + + @Override + public ValueExpression resolveVariable(String variable) { + return this.vars.get(variable); + } + + @Override + public ValueExpression setVariable(String variable, ValueExpression expression) { + return this.vars.put(variable, expression); + } + + // Safe cast. + @Override + @SuppressWarnings("unchecked") + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + this.vars = (Map<String, ValueExpression>) in.readObject(); + } + + @Override + public void writeExternal(ObjectOutput out) throws IOException { + out.writeObject(this.vars); + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/ArithmeticNode.java b/impl/src/main/java/com/sun/el/parser/ArithmeticNode.java new file mode 100644 index 0000000..36fce35 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/ArithmeticNode.java
@@ -0,0 +1,40 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public class ArithmeticNode extends SimpleNode { + + /** + * @param i + */ + public ArithmeticNode(int i) { + super(i); + } + + @Override + public Class getType(EvaluationContext ctx) throws ELException { + return Number.class; + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstAnd.java b/impl/src/main/java/com/sun/el/parser/AstAnd.java new file mode 100644 index 0000000..6e30086 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstAnd.java
@@ -0,0 +1,43 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstAnd extends BooleanNode { + public AstAnd(int id) { + super(id); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + Object obj = children[0].getValue(ctx); + Boolean b = coerceToBoolean(obj); + if (!b.booleanValue()) { + return b; + } + obj = children[1].getValue(ctx); + b = coerceToBoolean(obj); + return b; + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstAssign.java b/impl/src/main/java/com/sun/el/parser/AstAssign.java new file mode 100644 index 0000000..6ef715a --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstAssign.java
@@ -0,0 +1,35 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +public class AstAssign extends SimpleNode { + public AstAssign(int id) { + super(id); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + + Object value = children[1].getValue(ctx); + children[0].setValue(ctx, value); + return value; + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstBracketSuffix.java b/impl/src/main/java/com/sun/el/parser/AstBracketSuffix.java new file mode 100644 index 0000000..03454ab --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstBracketSuffix.java
@@ -0,0 +1,37 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @author Kin-man Chung + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstBracketSuffix extends SimpleNode { + public AstBracketSuffix(int id) { + super(id); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + return this.children[0].getValue(ctx); + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstChoice.java b/impl/src/main/java/com/sun/el/parser/AstChoice.java new file mode 100644 index 0000000..78f3fb4 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstChoice.java
@@ -0,0 +1,67 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstChoice extends SimpleNode { + public AstChoice(int id) { + super(id); + } + + @Override + public Class getType(EvaluationContext ctx) throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Boolean b0 = coerceToBoolean(obj0); + return this.children[((b0.booleanValue() ? 1 : 2))].getType(ctx); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Boolean b0 = coerceToBoolean(obj0); + return this.children[((b0.booleanValue() ? 1 : 2))].getValue(ctx); + } + + @Override + public boolean isReadOnly(EvaluationContext ctx) throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Boolean b0 = coerceToBoolean(obj0); + return this.children[((b0.booleanValue() ? 1 : 2))].isReadOnly(ctx); + } + + @Override + public void setValue(EvaluationContext ctx, Object value) throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Boolean b0 = coerceToBoolean(obj0); + this.children[((b0.booleanValue() ? 1 : 2))].setValue(ctx, value); + } + + @Override + public Object invoke(EvaluationContext ctx, Class[] paramTypes, Object[] paramValues) throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Boolean b0 = coerceToBoolean(obj0); + return this.children[((b0.booleanValue() ? 1 : 2))].invoke(ctx, paramTypes, paramValues); + } + +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstCompositeExpression.java b/impl/src/main/java/com/sun/el/parser/AstCompositeExpression.java new file mode 100644 index 0000000..b9f4a18 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstCompositeExpression.java
@@ -0,0 +1,52 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstCompositeExpression extends SimpleNode { + + public AstCompositeExpression(int id) { + super(id); + } + + @Override + public Class getType(EvaluationContext ctx) throws ELException { + return String.class; + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + StringBuffer sb = new StringBuffer(16); + Object obj = null; + if (this.children != null) { + for (int i = 0; i < this.children.length; i++) { + obj = this.children[i].getValue(ctx); + if (obj != null) { + sb.append(obj); + } + } + } + return sb.toString(); + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstConcat.java b/impl/src/main/java/com/sun/el/parser/AstConcat.java new file mode 100644 index 0000000..e0957f8 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstConcat.java
@@ -0,0 +1,35 @@ +/* + * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Kin-man Chung + */ +public final class AstConcat extends SimpleNode { + public AstConcat(int id) { + super(id); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + return children[0].getValue(ctx).toString() + children[1].getValue(ctx).toString(); + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstDeferredExpression.java b/impl/src/main/java/com/sun/el/parser/AstDeferredExpression.java new file mode 100644 index 0000000..9992b44 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstDeferredExpression.java
@@ -0,0 +1,51 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstDeferredExpression extends SimpleNode { + public AstDeferredExpression(int id) { + super(id); + } + + @Override + public Class getType(EvaluationContext ctx) throws ELException { + return this.children[0].getType(ctx); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + return this.children[0].getValue(ctx); + } + + @Override + public boolean isReadOnly(EvaluationContext ctx) throws ELException { + return this.children[0].isReadOnly(ctx); + } + + @Override + public void setValue(EvaluationContext ctx, Object value) throws ELException { + this.children[0].setValue(ctx, value); + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstDiv.java b/impl/src/main/java/com/sun/el/parser/AstDiv.java new file mode 100644 index 0000000..2979ac7 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstDiv.java
@@ -0,0 +1,39 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.ELArithmetic; +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstDiv extends ArithmeticNode { + public AstDiv(int id) { + super(id); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Object obj1 = this.children[1].getValue(ctx); + return ELArithmetic.divide(obj0, obj1); + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstDotSuffix.java b/impl/src/main/java/com/sun/el/parser/AstDotSuffix.java new file mode 100644 index 0000000..17083cc --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstDotSuffix.java
@@ -0,0 +1,36 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstDotSuffix extends SimpleNode { + public AstDotSuffix(int id) { + super(id); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + return this.image; + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstDynamicExpression.java b/impl/src/main/java/com/sun/el/parser/AstDynamicExpression.java new file mode 100644 index 0000000..a9d5f8f --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstDynamicExpression.java
@@ -0,0 +1,51 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstDynamicExpression extends SimpleNode { + public AstDynamicExpression(int id) { + super(id); + } + + @Override + public Class getType(EvaluationContext ctx) throws ELException { + return this.children[0].getType(ctx); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + return this.children[0].getValue(ctx); + } + + @Override + public boolean isReadOnly(EvaluationContext ctx) throws ELException { + return this.children[0].isReadOnly(ctx); + } + + @Override + public void setValue(EvaluationContext ctx, Object value) throws ELException { + this.children[0].setValue(ctx, value); + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstEmpty.java b/impl/src/main/java/com/sun/el/parser/AstEmpty.java new file mode 100644 index 0000000..847300a --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstEmpty.java
@@ -0,0 +1,56 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import java.util.Collection; +import java.util.Map; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstEmpty extends SimpleNode { + public AstEmpty(int id) { + super(id); + } + + @Override + public Class getType(EvaluationContext ctx) throws ELException { + return Boolean.class; + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + Object obj = this.children[0].getValue(ctx); + if (obj == null) { + return Boolean.TRUE; + } else if (obj instanceof String) { + return Boolean.valueOf(((String) obj).length() == 0); + } else if (obj instanceof Object[]) { + return Boolean.valueOf(((Object[]) obj).length == 0); + } else if (obj instanceof Collection) { + return Boolean.valueOf(((Collection) obj).isEmpty()); + } else if (obj instanceof Map) { + return Boolean.valueOf(((Map) obj).isEmpty()); + } + return Boolean.FALSE; + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstEqual.java b/impl/src/main/java/com/sun/el/parser/AstEqual.java new file mode 100644 index 0000000..ae26ef8 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstEqual.java
@@ -0,0 +1,38 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstEqual extends BooleanNode { + public AstEqual(int id) { + super(id); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Object obj1 = this.children[1].getValue(ctx); + return Boolean.valueOf(equals(obj0, obj1)); + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstFalse.java b/impl/src/main/java/com/sun/el/parser/AstFalse.java new file mode 100644 index 0000000..2c7ac5c --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstFalse.java
@@ -0,0 +1,36 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstFalse extends BooleanNode { + public AstFalse(int id) { + super(id); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + return Boolean.FALSE; + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstFloatingPoint.java b/impl/src/main/java/com/sun/el/parser/AstFloatingPoint.java new file mode 100644 index 0000000..bf3d003 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstFloatingPoint.java
@@ -0,0 +1,56 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import java.math.BigDecimal; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstFloatingPoint extends SimpleNode { + public AstFloatingPoint(int id) { + super(id); + } + + private Number number; + + public Number getFloatingPoint() { + if (this.number == null) { + try { + this.number = Double.valueOf(this.image); + } catch (ArithmeticException e0) { + this.number = new BigDecimal(this.image); + } + } + return this.number; + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + return this.getFloatingPoint(); + } + + @Override + public Class getType(EvaluationContext ctx) throws ELException { + return this.getFloatingPoint().getClass(); + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstFunction.java b/impl/src/main/java/com/sun/el/parser/AstFunction.java new file mode 100644 index 0000000..15fb1af --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstFunction.java
@@ -0,0 +1,194 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import javax.el.ELClass; +import javax.el.ELException; +import javax.el.FunctionMapper; +import javax.el.LambdaExpression; +import javax.el.ValueExpression; +import javax.el.VariableMapper; + +import com.sun.el.lang.EvaluationContext; +import com.sun.el.util.MessageFactory; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstFunction extends SimpleNode { + + protected String localName = ""; + + protected String prefix = ""; + + public AstFunction(int id) { + super(id); + } + + public String getLocalName() { + return localName; + } + + public String getOutputName() { + if (this.prefix.length() == 0) { + return this.localName; + } else { + return this.prefix + ":" + this.localName; + } + } + + public String getPrefix() { + return prefix; + } + + @Override + public Class getType(EvaluationContext ctx) throws ELException { + + FunctionMapper fnMapper = ctx.getFunctionMapper(); + + // quickly validate again for this request + if (fnMapper == null) { + throw new ELException(MessageFactory.get("error.fnMapper.null")); + } + Method m = fnMapper.resolveFunction(this.prefix, this.localName); + if (m == null) { + throw new ELException(MessageFactory.get("error.fnMapper.method", this.getOutputName())); + } + return m.getReturnType(); + } + + /* + * Find the object associated with the given name. Return null if the there is no such object. + */ + private Object findValue(EvaluationContext ctx, String name) { + Object value; + // First check if this is a Lambda argument + if (ctx.isLambdaArgument(name)) { + return ctx.getLambdaArgument(name); + } + + // Next check if this an Jakarta Expression variable + VariableMapper varMapper = ctx.getVariableMapper(); + if (varMapper != null) { + ValueExpression expr = varMapper.resolveVariable(name); + if (expr != null) { + return expr.getValue(ctx.getELContext()); + } + } + // Check if this is resolvable by an ELResolver + ctx.setPropertyResolved(false); + Object ret = ctx.getELResolver().getValue(ctx, null, name); + if (ctx.isPropertyResolved()) { + return ret; + } + return null; + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + + // Check to see if a function is a bean that is a Lambdaexpression. + // If so, invoke it. Also allow for the case that a Lambda expression + // can return another Lambda expression. + if (prefix.length() == 0) { + Object val = findValue(ctx, this.localName); + // Check the case of repeated lambda invocation, such as f()()() + + if ((val != null) && (val instanceof LambdaExpression)) { + for (int i = 0; i < this.children.length; i++) { + Object[] params = ((AstMethodArguments) this.children[i]).getParameters(ctx); + if (!(val instanceof LambdaExpression)) { + throw new ELException(MessageFactory.get("error.function.syntax", getOutputName())); + } + val = ((LambdaExpression) val).invoke(ctx, params); + } + return val; + } + } + + FunctionMapper fnMapper = ctx.getFunctionMapper(); + + Method m = null; + if (fnMapper != null) { + m = fnMapper.resolveFunction(this.prefix, this.localName); + } + if (m == null) { + if (this.prefix.length() == 0 && ctx.getImportHandler() != null) { + Class<?> c = null; + ; + // Check if this is a constructor call for an imported class + c = ctx.getImportHandler().resolveClass(this.localName); + String methodName = null; + if (c != null) { + methodName = "<init>"; + } else { + // Check if this is a imported static method + c = ctx.getImportHandler().resolveStatic(this.localName); + methodName = this.localName; + ; + } + if (c != null) { + // Use StaticFieldELResolver to invoke the constructor or the + // static method. + Object[] params = ((AstMethodArguments) this.children[0]).getParameters(ctx); + return ctx.getELResolver().invoke(ctx, new ELClass(c), methodName, null, params); + } + } + // quickly validate for this request + if (fnMapper == null) { + throw new ELException(MessageFactory.get("error.fnMapper.null")); + } + throw new ELException(MessageFactory.get("error.fnMapper.method", this.getOutputName())); + } + + Class[] paramTypes = m.getParameterTypes(); + Object[] params = ((AstMethodArguments) this.children[0]).getParameters(ctx); + Object result = null; + for (int i = 0; i < params.length; i++) { + try { + params[i] = ctx.convertToType(params[i], paramTypes[i]); + } catch (ELException ele) { + throw new ELException(MessageFactory.get("error.function", this.getOutputName()), ele); + } + } + try { + result = m.invoke(null, params); + } catch (IllegalAccessException iae) { + throw new ELException(MessageFactory.get("error.function", this.getOutputName()), iae); + } catch (InvocationTargetException ite) { + throw new ELException(MessageFactory.get("error.function", this.getOutputName()), ite.getCause()); + } + return result; + } + + public void setLocalName(String localName) { + this.localName = localName; + } + + public void setPrefix(String prefix) { + this.prefix = prefix; + } + + @Override + public String toString() { + return ELParserTreeConstants.jjtNodeName[id] + "[" + this.getOutputName() + "]"; + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstGreaterThan.java b/impl/src/main/java/com/sun/el/parser/AstGreaterThan.java new file mode 100644 index 0000000..2de0bd9 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstGreaterThan.java
@@ -0,0 +1,44 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstGreaterThan extends BooleanNode { + public AstGreaterThan(int id) { + super(id); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + Object obj0 = this.children[0].getValue(ctx); + if (obj0 == null) { + return Boolean.FALSE; + } + Object obj1 = this.children[1].getValue(ctx); + if (obj1 == null) { + return Boolean.FALSE; + } + return (compare(obj0, obj1) > 0) ? Boolean.TRUE : Boolean.FALSE; + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstGreaterThanEqual.java b/impl/src/main/java/com/sun/el/parser/AstGreaterThanEqual.java new file mode 100644 index 0000000..06252c0 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstGreaterThanEqual.java
@@ -0,0 +1,44 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstGreaterThanEqual extends BooleanNode { + public AstGreaterThanEqual(int id) { + super(id); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Object obj1 = this.children[1].getValue(ctx); + if (obj0 == obj1) { + return Boolean.TRUE; + } + if (obj0 == null || obj1 == null) { + return Boolean.FALSE; + } + return (compare(obj0, obj1) >= 0) ? Boolean.TRUE : Boolean.FALSE; + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstIdentifier.java b/impl/src/main/java/com/sun/el/parser/AstIdentifier.java new file mode 100644 index 0000000..a5a149b --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstIdentifier.java
@@ -0,0 +1,200 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELClass; +import javax.el.ELException; +import javax.el.ELResolver; +import javax.el.MethodExpression; +import javax.el.MethodInfo; +import javax.el.MethodNotFoundException; +import javax.el.PropertyNotWritableException; +import javax.el.ValueExpression; +import javax.el.ValueReference; +import javax.el.VariableMapper; + +import com.sun.el.lang.ELSupport; +import com.sun.el.lang.EvaluationContext; +import com.sun.el.util.MessageFactory; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @author Kin-man Chung + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstIdentifier extends SimpleNode { + public AstIdentifier(int id) { + super(id); + } + + @Override + public Class getType(EvaluationContext ctx) throws ELException { + // First check if this is a lambda argument + if (ctx.isLambdaArgument(this.image)) { + return Object.class; + } + VariableMapper varMapper = ctx.getVariableMapper(); + if (varMapper != null) { + ValueExpression expr = varMapper.resolveVariable(this.image); + if (expr != null) { + return expr.getType(ctx.getELContext()); + } + } + ctx.setPropertyResolved(false); + Class ret = ctx.getELResolver().getType(ctx, null, this.image); + if (!ctx.isPropertyResolved()) { + ELSupport.throwUnhandled(null, this.image); + } + return ret; + } + + @Override + public ValueReference getValueReference(EvaluationContext ctx) throws ELException { + VariableMapper varMapper = ctx.getVariableMapper(); + if (varMapper != null) { + ValueExpression expr = varMapper.resolveVariable(this.image); + if (expr != null) { + return expr.getValueReference(ctx.getELContext()); + } + } + return new ValueReference(null, this.image); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + // First check if this is a lambda argument + if (ctx.isLambdaArgument(this.image)) { + return ctx.getLambdaArgument(this.image); + } + VariableMapper varMapper = ctx.getVariableMapper(); + if (varMapper != null) { + ValueExpression expr = varMapper.resolveVariable(this.image); + if (expr != null) { + return expr.getValue(ctx.getELContext()); + } + } + ctx.setPropertyResolved(false); + Object ret = ctx.getELResolver().getValue(ctx, null, this.image); + if (!ctx.isPropertyResolved()) { + // Check if this is an imported static field + if (ctx.getImportHandler() != null) { + Class<?> c = ctx.getImportHandler().resolveStatic(this.image); + if (c != null) { + return ctx.getELResolver().getValue(ctx, new ELClass(c), this.image); + } + } + ELSupport.throwUnhandled(null, this.image); + } + return ret; + } + + @Override + public boolean isReadOnly(EvaluationContext ctx) throws ELException { + // Lambda arguments are read only. + if (ctx.isLambdaArgument(this.image)) { + return true; + } + VariableMapper varMapper = ctx.getVariableMapper(); + if (varMapper != null) { + ValueExpression expr = varMapper.resolveVariable(this.image); + if (expr != null) { + return expr.isReadOnly(ctx.getELContext()); + } + } + ctx.setPropertyResolved(false); + boolean ret = ctx.getELResolver().isReadOnly(ctx, null, this.image); + if (!ctx.isPropertyResolved()) { + ELSupport.throwUnhandled(null, this.image); + } + return ret; + } + + @Override + public void setValue(EvaluationContext ctx, Object value) throws ELException { + // First check if this is a lambda argument + if (ctx.isLambdaArgument(this.image)) { + throw new PropertyNotWritableException(MessageFactory.get("error.lambda.parameter.readonly", this.image)); + } + VariableMapper varMapper = ctx.getVariableMapper(); + if (varMapper != null) { + ValueExpression expr = varMapper.resolveVariable(this.image); + if (expr != null) { + expr.setValue(ctx.getELContext(), value); + return; + } + } + ctx.setPropertyResolved(false); + ELResolver elResolver = ctx.getELResolver(); + elResolver.setValue(ctx, null, this.image, value); + if (!ctx.isPropertyResolved()) { + ELSupport.throwUnhandled(null, this.image); + } + } + + private Object invokeTarget(EvaluationContext ctx, Object target, Object[] paramValues) throws ELException { + if (target instanceof MethodExpression) { + MethodExpression me = (MethodExpression) target; + return me.invoke(ctx.getELContext(), paramValues); + } else if (target == null) { + throw new MethodNotFoundException("Identity '" + this.image + "' was null and was unable to invoke"); + } else { + throw new ELException( + "Identity '" + this.image + "' does not reference a MethodExpression instance, returned type: " + target.getClass().getName()); + } + } + + @Override + public Object invoke(EvaluationContext ctx, Class[] paramTypes, Object[] paramValues) throws ELException { + return this.getMethodExpression(ctx).invoke(ctx.getELContext(), paramValues); + } + + @Override + public MethodInfo getMethodInfo(EvaluationContext ctx, Class[] paramTypes) throws ELException { + return this.getMethodExpression(ctx).getMethodInfo(ctx.getELContext()); + } + + private MethodExpression getMethodExpression(EvaluationContext ctx) throws ELException { + Object obj = null; + + // case A: ValueExpression exists, getValue which must + // be a MethodExpression + VariableMapper varMapper = ctx.getVariableMapper(); + ValueExpression ve = null; + if (varMapper != null) { + ve = varMapper.resolveVariable(this.image); + if (ve != null) { + obj = ve.getValue(ctx); + } + } + + // case B: evaluate the identity against the ELResolver, again, must be + // a MethodExpression to be able to invoke + if (ve == null) { + ctx.setPropertyResolved(false); + obj = ctx.getELResolver().getValue(ctx, null, this.image); + } + + // finally provide helpful hints + if (obj instanceof MethodExpression) { + return (MethodExpression) obj; + } else if (obj == null) { + throw new MethodNotFoundException("Identity '" + this.image + "' was null and was unable to invoke"); + } else { + throw new ELException("Identity '" + this.image + "' does not reference a MethodExpression instance, returned type: " + obj.getClass().getName()); + } + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstInteger.java b/impl/src/main/java/com/sun/el/parser/AstInteger.java new file mode 100644 index 0000000..8c6d7c7 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstInteger.java
@@ -0,0 +1,56 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import java.math.BigInteger; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstInteger extends SimpleNode { + public AstInteger(int id) { + super(id); + } + + private Number number; + + protected Number getInteger() { + if (this.number == null) { + try { + this.number = Long.valueOf(this.image); + } catch (ArithmeticException e1) { + this.number = new BigInteger(this.image); + } + } + return number; + } + + @Override + public Class getType(EvaluationContext ctx) throws ELException { + return this.getInteger().getClass(); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + return this.getInteger(); + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstLambdaExpression.java b/impl/src/main/java/com/sun/el/parser/AstLambdaExpression.java new file mode 100644 index 0000000..1349899 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstLambdaExpression.java
@@ -0,0 +1,62 @@ +/* + * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import java.util.List; + +import javax.el.ELException; +import javax.el.LambdaExpression; +import javax.el.ValueExpression; + +import com.sun.el.ValueExpressionImpl; +import com.sun.el.lang.EvaluationContext; +import com.sun.el.util.MessageFactory; + +/** + * @author Kin-man Chung + */ +public class AstLambdaExpression extends SimpleNode { + + public AstLambdaExpression(int id) { + super(id); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + // Create a lambda expression + ValueExpression expr = new ValueExpressionImpl("#{Lambda Expression}", this.children[1], ctx.getFunctionMapper(), ctx.getVariableMapper(), null); + List<String> parameters = ((AstLambdaParameters) this.children[0]).getParameters(); + LambdaExpression lambda = new LambdaExpression(parameters, expr); + if (this.children.length <= 2) { + return lambda; + } + + // There are arguments following the lambda exprn, invoke it now. + Object ret = null; + for (int i = 2; i < this.children.length; i++) { + if (ret != null) { + if (!(ret instanceof LambdaExpression)) { + throw new ELException(MessageFactory.get("error.lambda.call")); + } + lambda = (LambdaExpression) ret; + } + AstMethodArguments args = (AstMethodArguments) this.children[i]; + ret = lambda.invoke(ctx, args.getParameters(ctx)); + } + return ret; + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstLambdaParameters.java b/impl/src/main/java/com/sun/el/parser/AstLambdaParameters.java new file mode 100644 index 0000000..ee5875c --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstLambdaParameters.java
@@ -0,0 +1,40 @@ +/* + * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Kin-man Chung + */ + +public class AstLambdaParameters extends SimpleNode { + public AstLambdaParameters(int id) { + super(id); + } + + List<String> getParameters() { + List<String> parameters = new ArrayList<String>(); + if (children != null) { + for (Node child : children) { + parameters.add(child.getImage()); + } + } + return parameters; + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstLessThan.java b/impl/src/main/java/com/sun/el/parser/AstLessThan.java new file mode 100644 index 0000000..c1bd471 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstLessThan.java
@@ -0,0 +1,44 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstLessThan extends BooleanNode { + public AstLessThan(int id) { + super(id); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + Object obj0 = this.children[0].getValue(ctx); + if (obj0 == null) { + return Boolean.FALSE; + } + Object obj1 = this.children[1].getValue(ctx); + if (obj1 == null) { + return Boolean.FALSE; + } + return (compare(obj0, obj1) < 0) ? Boolean.TRUE : Boolean.FALSE; + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstLessThanEqual.java b/impl/src/main/java/com/sun/el/parser/AstLessThanEqual.java new file mode 100644 index 0000000..8f2c837 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstLessThanEqual.java
@@ -0,0 +1,44 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstLessThanEqual extends BooleanNode { + public AstLessThanEqual(int id) { + super(id); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Object obj1 = this.children[1].getValue(ctx); + if (obj0 == obj1) { + return Boolean.TRUE; + } + if (obj0 == null || obj1 == null) { + return Boolean.FALSE; + } + return (compare(obj0, obj1) <= 0) ? Boolean.TRUE : Boolean.FALSE; + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstListData.java b/impl/src/main/java/com/sun/el/parser/AstListData.java new file mode 100644 index 0000000..7dc2952 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstListData.java
@@ -0,0 +1,40 @@ +/* + * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import java.util.ArrayList; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Kin-man Chung + */ +public class AstListData extends SimpleNode { + public AstListData(int id) { + super(id); + } + + @Override + public Object getValue(EvaluationContext ctx) { + ArrayList<Object> list = new ArrayList<Object>(); + int paramCount = this.jjtGetNumChildren(); + for (int i = 0; i < paramCount; i++) { + list.add(this.children[i].getValue(ctx)); + } + return list; + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstLiteralExpression.java b/impl/src/main/java/com/sun/el/parser/AstLiteralExpression.java new file mode 100644 index 0000000..9672246 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstLiteralExpression.java
@@ -0,0 +1,63 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstLiteralExpression extends SimpleNode { + public AstLiteralExpression(int id) { + super(id); + } + + @Override + public Class getType(EvaluationContext ctx) throws ELException { + return String.class; + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + return this.image; + } + + @Override + public void setImage(String image) { + if (image.indexOf('\\') == -1) { + this.image = image; + return; + } + int size = image.length(); + StringBuffer buf = new StringBuffer(size); + for (int i = 0; i < size; i++) { + char c = image.charAt(i); + if (c == '\\' && i + 1 < size) { + char c1 = image.charAt(i + 1); + if (c1 == '\\' || c1 == '"' || c1 == '\'' || c1 == '#' || c1 == '$') { + c = c1; + i++; + } + } + buf.append(c); + } + this.image = buf.toString(); + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstMapData.java b/impl/src/main/java/com/sun/el/parser/AstMapData.java new file mode 100644 index 0000000..41435c8 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstMapData.java
@@ -0,0 +1,59 @@ +/* + * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import java.util.HashMap; +import java.util.HashSet; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Kin-man Chung + */ +public class AstMapData extends SimpleNode { + public AstMapData(int id) { + super(id); + } + + @Override + public Object getValue(EvaluationContext ctx) { + HashSet<Object> set = new HashSet<Object>(); + HashMap<Object, Object> map = new HashMap<Object, Object>(); + + int paramCount = this.jjtGetNumChildren(); + for (int i = 0; i < paramCount; i++) { + Node entry = this.children[i]; + Object v1 = entry.jjtGetChild(0).getValue(ctx); + if (entry.jjtGetNumChildren() > 1) { + // expr: expr + map.put(v1, entry.jjtGetChild(1).getValue(ctx)); + } else { + set.add(v1); + } + } + // It is error to have mixed set/map entries + if (set.size() > 0 && map.size() > 0) { + throw new ELException("Cannot mix set entry with map entry."); + } + if (map.size() > 0) { + return map; + } + return set; + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstMapEntry.java b/impl/src/main/java/com/sun/el/parser/AstMapEntry.java new file mode 100644 index 0000000..c55caa6 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstMapEntry.java
@@ -0,0 +1,26 @@ +/* + * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +/** + * @author Kin-man Chung + */ +public class AstMapEntry extends SimpleNode { + public AstMapEntry(int id) { + super(id); + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstMethodArguments.java b/impl/src/main/java/com/sun/el/parser/AstMethodArguments.java new file mode 100644 index 0000000..24ef021 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstMethodArguments.java
@@ -0,0 +1,57 @@ +/* + * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Kin-man Chung + */ +public class AstMethodArguments extends SimpleNode { + public AstMethodArguments(int id) { + super(id); + } + + Class<?>[] getParamTypes() { + return null; + } + + public Object[] getParameters(EvaluationContext ctx) throws ELException { + + if (this.children == null) { + return new Object[] {}; + } + + Object[] obj = new Object[this.children.length]; + for (int i = 0; i < obj.length; i++) { + obj[i] = this.children[i].getValue(ctx); + } + return obj; + } + + public int getParameterCount() { + return this.children == null ? 0 : this.children.length; + } + + @Override + public boolean isParametersProvided() { + return true; + } + +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstMinus.java b/impl/src/main/java/com/sun/el/parser/AstMinus.java new file mode 100644 index 0000000..11fdf84 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstMinus.java
@@ -0,0 +1,39 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.ELArithmetic; +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstMinus extends ArithmeticNode { + public AstMinus(int id) { + super(id); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Object obj1 = this.children[1].getValue(ctx); + return ELArithmetic.subtract(obj0, obj1); + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstMod.java b/impl/src/main/java/com/sun/el/parser/AstMod.java new file mode 100644 index 0000000..d69b928 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstMod.java
@@ -0,0 +1,39 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.ELArithmetic; +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstMod extends ArithmeticNode { + public AstMod(int id) { + super(id); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Object obj1 = this.children[1].getValue(ctx); + return ELArithmetic.mod(obj0, obj1); + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstMult.java b/impl/src/main/java/com/sun/el/parser/AstMult.java new file mode 100644 index 0000000..8ee4751 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstMult.java
@@ -0,0 +1,39 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.ELArithmetic; +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstMult extends ArithmeticNode { + public AstMult(int id) { + super(id); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Object obj1 = this.children[1].getValue(ctx); + return ELArithmetic.multiply(obj0, obj1); + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstNegative.java b/impl/src/main/java/com/sun/el/parser/AstNegative.java new file mode 100644 index 0000000..e4b4465 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstNegative.java
@@ -0,0 +1,81 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstNegative extends SimpleNode { + public AstNegative(int id) { + super(id); + } + + @Override + public Class getType(EvaluationContext ctx) throws ELException { + return Number.class; + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + Object obj = this.children[0].getValue(ctx); + + if (obj == null) { + return Long.valueOf(0); + } + if (obj instanceof BigDecimal) { + return ((BigDecimal) obj).negate(); + } + if (obj instanceof BigInteger) { + return ((BigInteger) obj).negate(); + } + if (obj instanceof String) { + if (isStringFloat((String) obj)) { + return Double.valueOf(-Double.parseDouble((String) obj)); + } + return Long.valueOf(-Long.parseLong((String) obj)); + } + Class type = obj.getClass(); + if (obj instanceof Long || Long.TYPE == type) { + return Long.valueOf(-((Long) obj).longValue()); + } + if (obj instanceof Double || Double.TYPE == type) { + return Double.valueOf(-((Double) obj).doubleValue()); + } + if (obj instanceof Integer || Integer.TYPE == type) { + return Integer.valueOf(-((Integer) obj).intValue()); + } + if (obj instanceof Float || Float.TYPE == type) { + return Float.valueOf(-((Float) obj).floatValue()); + } + if (obj instanceof Short || Short.TYPE == type) { + return Short.valueOf((short) -((Short) obj).shortValue()); + } + if (obj instanceof Byte || Byte.TYPE == type) { + return Byte.valueOf((byte) -((Byte) obj).byteValue()); + } + Long num = (Long) coerceToNumber(obj, Long.class); + return Long.valueOf(-num.longValue()); + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstNot.java b/impl/src/main/java/com/sun/el/parser/AstNot.java new file mode 100644 index 0000000..5f0886b --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstNot.java
@@ -0,0 +1,43 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstNot extends SimpleNode { + public AstNot(int id) { + super(id); + } + + @Override + public Class getType(EvaluationContext ctx) throws ELException { + return Boolean.class; + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + Object obj = this.children[0].getValue(ctx); + Boolean b = coerceToBoolean(obj); + return Boolean.valueOf(!b.booleanValue()); + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstNotEqual.java b/impl/src/main/java/com/sun/el/parser/AstNotEqual.java new file mode 100644 index 0000000..359f479 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstNotEqual.java
@@ -0,0 +1,38 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstNotEqual extends BooleanNode { + public AstNotEqual(int id) { + super(id); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Object obj1 = this.children[1].getValue(ctx); + return Boolean.valueOf(!equals(obj0, obj1)); + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstNull.java b/impl/src/main/java/com/sun/el/parser/AstNull.java new file mode 100644 index 0000000..548a387 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstNull.java
@@ -0,0 +1,41 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstNull extends SimpleNode { + public AstNull(int id) { + super(id); + } + + @Override + public Class getType(EvaluationContext ctx) throws ELException { + return null; + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + return null; + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstOr.java b/impl/src/main/java/com/sun/el/parser/AstOr.java new file mode 100644 index 0000000..2fc12d4 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstOr.java
@@ -0,0 +1,43 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstOr extends BooleanNode { + public AstOr(int id) { + super(id); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + Object obj = this.children[0].getValue(ctx); + Boolean b = coerceToBoolean(obj); + if (b.booleanValue()) { + return b; + } + obj = this.children[1].getValue(ctx); + b = coerceToBoolean(obj); + return b; + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstPlus.java b/impl/src/main/java/com/sun/el/parser/AstPlus.java new file mode 100644 index 0000000..2592188 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstPlus.java
@@ -0,0 +1,40 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.ELArithmetic; +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @author Kin-man Chung + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstPlus extends ArithmeticNode { + public AstPlus(int id) { + super(id); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + Object obj0 = this.children[0].getValue(ctx); + Object obj1 = this.children[1].getValue(ctx); + return ELArithmetic.add(obj0, obj1); + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstSemiColon.java b/impl/src/main/java/com/sun/el/parser/AstSemiColon.java new file mode 100644 index 0000000..275aaba --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstSemiColon.java
@@ -0,0 +1,42 @@ +/* + * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Kin-man Chung + */ +public class AstSemiColon extends SimpleNode { + public AstSemiColon(int id) { + super(id); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + this.children[0].getValue(ctx); + return this.children[1].getValue(ctx); + } + + @Override + public void setValue(EvaluationContext ctx, Object value) throws ELException { + this.children[0].getValue(ctx); + this.children[1].setValue(ctx, value); + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstString.java b/impl/src/main/java/com/sun/el/parser/AstString.java new file mode 100644 index 0000000..77a7ad1 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstString.java
@@ -0,0 +1,72 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstString extends SimpleNode { + public AstString(int id) { + super(id); + } + + private String string; + + public String getString() { + if (this.string == null) { + this.string = this.image.substring(1, this.image.length() - 1); + } + return this.string; + } + + @Override + public Class getType(EvaluationContext ctx) throws ELException { + return String.class; + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + return this.getString(); + } + + @Override + public void setImage(String image) { + if (image.indexOf('\\') == -1) { + this.image = image; + return; + } + int size = image.length(); + StringBuffer buf = new StringBuffer(size); + for (int i = 0; i < size; i++) { + char c = image.charAt(i); + if (c == '\\' && i + 1 < size) { + char c1 = image.charAt(i + 1); + if (c1 == '\\' || c1 == '"' || c1 == '\'' || c1 == '#' || c1 == '$') { + c = c1; + i++; + } + } + buf.append(c); + } + this.image = buf.toString(); + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstTrue.java b/impl/src/main/java/com/sun/el/parser/AstTrue.java new file mode 100644 index 0000000..607e6bd --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstTrue.java
@@ -0,0 +1,36 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstTrue extends BooleanNode { + public AstTrue(int id) { + super(id); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + return Boolean.TRUE; + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/AstValue.java b/impl/src/main/java/com/sun/el/parser/AstValue.java new file mode 100644 index 0000000..dfe0f82 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/AstValue.java
@@ -0,0 +1,268 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import java.lang.reflect.Method; + +import javax.el.ELClass; +import javax.el.ELException; +import javax.el.ELResolver; +import javax.el.ImportHandler; +import javax.el.MethodInfo; +import javax.el.PropertyNotFoundException; +import javax.el.PropertyNotWritableException; +import javax.el.ValueReference; + +import com.sun.el.lang.ELSupport; +import com.sun.el.lang.EvaluationContext; +import com.sun.el.util.MessageFactory; +import com.sun.el.util.ReflectionUtil; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @author Kin-man Chung + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class AstValue extends SimpleNode { + + protected static class Target { + protected Object base; + protected Node suffixNode; + + Target(Object base, Node suffixNode) { + this.base = base; + this.suffixNode = suffixNode; + } + + boolean isMethodCall() { + return getArguments(suffixNode) != null; + } + } + + public AstValue(int id) { + super(id); + } + + @Override + public Class getType(EvaluationContext ctx) throws ELException { + Target t = getTarget(ctx); + if (t.isMethodCall()) { + return null; + } + Object property = t.suffixNode.getValue(ctx); + ctx.setPropertyResolved(false); + Class ret = ctx.getELResolver().getType(ctx, t.base, property); + if (!ctx.isPropertyResolved()) { + ELSupport.throwUnhandled(t.base, property); + } + return ret; + } + + @Override + public ValueReference getValueReference(EvaluationContext ctx) throws ELException { + Target t = getTarget(ctx); + if (t.isMethodCall()) { + return null; + } + Object property = t.suffixNode.getValue(ctx); + return new ValueReference(t.base, property); + } + + private static AstMethodArguments getArguments(Node n) { + if (n instanceof AstDotSuffix && n.jjtGetNumChildren() > 0) { + return (AstMethodArguments) n.jjtGetChild(0); + } + if (n instanceof AstBracketSuffix && n.jjtGetNumChildren() > 1) { + return (AstMethodArguments) n.jjtGetChild(1); + } + return null; + } + + private Object getValue(Object base, Node child, EvaluationContext ctx) throws ELException { + + Object value = null; + ELResolver resolver = ctx.getELResolver(); + Object property = child.getValue(ctx); + AstMethodArguments args = getArguments(child); + if (args != null) { + // This is a method call + if (!(property instanceof String)) { + throw new ELException(MessageFactory.get("error.method.name", property)); + } + Class<?>[] paramTypes = args.getParamTypes(); + Object[] params = args.getParameters(ctx); + + ctx.setPropertyResolved(false); + value = resolver.invoke(ctx, base, property, paramTypes, params); + } else { + if (property != null) { + ctx.setPropertyResolved(false); + value = resolver.getValue(ctx, base, property); + if (!ctx.isPropertyResolved()) { + ELSupport.throwUnhandled(base, property); + } + } + } + return value; + } + + private Object getBase(EvaluationContext ctx) { + try { + return this.children[0].getValue(ctx); + } catch (PropertyNotFoundException ex) { + // Next check if the base is an imported class + if (this.children[0] instanceof AstIdentifier) { + String name = ((AstIdentifier) this.children[0]).image; + ImportHandler importHandler = ctx.getImportHandler(); + if (importHandler != null) { + Class<?> c = importHandler.resolveClass(name); + if (c != null) { + return new ELClass(c); + } + } + } + throw ex; + } + } + + private Target getTarget(EvaluationContext ctx) throws ELException { + // evaluate expr-a to value-a + Object base = getBase(ctx); + + // if our base is null (we know there are more properites to evaluate) + if (base == null) { + throw new PropertyNotFoundException(MessageFactory.get("error.unreachable.base", this.children[0].getImage())); + } + + // set up our start/end + Object property = null; + int propCount = this.jjtGetNumChildren() - 1; + int i = 1; + + // evaluate any properties before our target + if (propCount > 1) { + while (base != null && i < propCount) { + base = getValue(base, this.children[i], ctx); + i++; + } + // if we are in this block, we have more properties to resolve, + // but our base was null + if (base == null) { + throw new PropertyNotFoundException(MessageFactory.get("error.unreachable.property", property)); + } + } + return new Target(base, this.children[propCount]); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + Object base = getBase(ctx); + int propCount = this.jjtGetNumChildren(); + int i = 1; + while (base != null && i < propCount) { + base = getValue(base, this.children[i], ctx); + i++; + } + return base; + } + + @Override + public boolean isReadOnly(EvaluationContext ctx) throws ELException { + Target t = getTarget(ctx); + if (t.isMethodCall()) { + return true; + } + Object property = t.suffixNode.getValue(ctx); + ctx.setPropertyResolved(false); + boolean ret = ctx.getELResolver().isReadOnly(ctx, t.base, property); + if (!ctx.isPropertyResolved()) { + ELSupport.throwUnhandled(t.base, property); + } + return ret; + } + + @Override + public void setValue(EvaluationContext ctx, Object value) throws ELException { + Target t = getTarget(ctx); + if (t.isMethodCall()) { + throw new PropertyNotWritableException(MessageFactory.get("error.syntax.set")); + } + Object property = t.suffixNode.getValue(ctx); + ELResolver elResolver = ctx.getELResolver(); + + /* + * Note by kchung 10/2013 The spec does not say if the value should be cocerced to the target type before setting the + * value to the target. The conversion is kept here to be backward compatible. + */ + ctx.setPropertyResolved(false); + Class<?> targetType = elResolver.getType(ctx, t.base, property); + if (ctx.isPropertyResolved()) { + ctx.setPropertyResolved(false); + Object targetValue = elResolver.convertToType(ctx, value, targetType); + + if (ctx.isPropertyResolved()) { + value = targetValue; + } else { + if (value != null || targetType.isPrimitive()) { + value = ELSupport.coerceToType(value, targetType); + } + } + } + + ctx.setPropertyResolved(false); + elResolver.setValue(ctx, t.base, property, value); + if (!ctx.isPropertyResolved()) { + ELSupport.throwUnhandled(t.base, property); + } + } + + @Override + public MethodInfo getMethodInfo(EvaluationContext ctx, Class[] paramTypes) throws ELException { + Target t = getTarget(ctx); + if (t.isMethodCall()) { + return null; + } + Object property = t.suffixNode.getValue(ctx); + Method m = ReflectionUtil.findMethod(t.base.getClass(), property.toString(), paramTypes, null); + return new MethodInfo(m.getName(), m.getReturnType(), m.getParameterTypes()); + } + + @Override + public Object invoke(EvaluationContext ctx, Class[] paramTypes, Object[] paramValues) throws ELException { + Target t = getTarget(ctx); + if (t.isMethodCall()) { + AstMethodArguments args = getArguments(t.suffixNode); + // Always use the param types in the expression, and ignore those + // specified elsewhere, such as TLD + paramTypes = args.getParamTypes(); + Object[] params = args.getParameters(ctx); + String method = (String) t.suffixNode.getValue(ctx); + + ctx.setPropertyResolved(false); + ELResolver resolver = ctx.getELResolver(); + return resolver.invoke(ctx, t.base, method, paramTypes, params); + } + Object property = t.suffixNode.getValue(ctx); + Method m = ReflectionUtil.findMethod(t.base.getClass(), property.toString(), paramTypes, paramValues); + return ReflectionUtil.invokeMethod(ctx, m, t.base, paramValues); + } + + @Override + public boolean isParametersProvided() { + return getArguments(this.children[this.jjtGetNumChildren() - 1]) != null; + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/BooleanNode.java b/impl/src/main/java/com/sun/el/parser/BooleanNode.java new file mode 100644 index 0000000..5f4dec1 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/BooleanNode.java
@@ -0,0 +1,39 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +import com.sun.el.lang.EvaluationContext; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public class BooleanNode extends SimpleNode { + /** + * @param i + */ + public BooleanNode(int i) { + super(i); + } + + @Override + public Class getType(EvaluationContext ctx) throws ELException { + return Boolean.class; + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/ELParser.java b/impl/src/main/java/com/sun/el/parser/ELParser.java new file mode 100644 index 0000000..4d76641 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/ELParser.java
@@ -0,0 +1,3782 @@ +/* + * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +/* Generated By:JJTree&JavaCC: Do not edit this line. ELParser.java */ +package com.sun.el.parser; + +import java.io.StringReader; + +import javax.el.ELException; + +public class ELParser/* @bgen(jjtree) */ implements ELParserTreeConstants, ELParserConstants {/* @bgen(jjtree) */ + protected JJTELParserState jjtree = new JJTELParserState(); + + public static Node parse(String ref) throws ELException { + try { + return (new ELParser(new StringReader(ref))).CompositeExpression(); + } catch (ParseException pe) { + throw new ELException(pe.getMessage()); + } + } + + /* + * CompositeExpression Allow most flexible parsing, restrict by examining type of returned node + */ + final public AstCompositeExpression CompositeExpression() throws ParseException { + /* @bgen(jjtree) CompositeExpression */ + AstCompositeExpression jjtn000 = new AstCompositeExpression(JJTCOMPOSITEEXPRESSION); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + try { + label_1: while (true) { + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case LITERAL_EXPRESSION: + case START_DYNAMIC_EXPRESSION: + case START_DEFERRED_EXPRESSION: + ; + break; + default: + jj_la1[0] = jj_gen; + break label_1; + } + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case START_DEFERRED_EXPRESSION: + DeferredExpression(); + break; + case START_DYNAMIC_EXPRESSION: + DynamicExpression(); + break; + case LITERAL_EXPRESSION: + LiteralExpression(); + break; + default: + jj_la1[1] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + jj_consume_token(0); + jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + { + if (true) { + return jjtn000; + } + } + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte000; + } + } + } + if (jjte000 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte000; + } + } + } + { + if (true) { + throw (Error) jjte000; + } + } + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + throw new Error("Missing return statement in function"); + } + + /* + * LiteralExpression Non-EL Expression blocks + */ + final public void LiteralExpression() throws ParseException { + /* @bgen(jjtree) LiteralExpression */ + AstLiteralExpression jjtn000 = new AstLiteralExpression(JJTLITERALEXPRESSION); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + Token t = null; + try { + t = jj_consume_token(LITERAL_EXPRESSION); + jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + jjtn000.setImage(t.image); + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + + /* + * DeferredExpression #{..} Expressions + */ + final public void DeferredExpression() throws ParseException { + /* @bgen(jjtree) DeferredExpression */ + AstDeferredExpression jjtn000 = new AstDeferredExpression(JJTDEFERREDEXPRESSION); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + try { + jj_consume_token(START_DEFERRED_EXPRESSION); + Expression(); + jj_consume_token(RCURL); + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte000; + } + } + } + if (jjte000 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte000; + } + } + } + { + if (true) { + throw (Error) jjte000; + } + } + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + + /* + * DynamicExpression ${..} Expressions + */ + final public void DynamicExpression() throws ParseException { + /* @bgen(jjtree) DynamicExpression */ + AstDynamicExpression jjtn000 = new AstDynamicExpression(JJTDYNAMICEXPRESSION); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + try { + jj_consume_token(START_DYNAMIC_EXPRESSION); + Expression(); + jj_consume_token(RCURL); + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte000; + } + } + } + if (jjte000 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte000; + } + } + } + { + if (true) { + throw (Error) jjte000; + } + } + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + + /* + * Jakarta Expression Root + */ + final public void Expression() throws ParseException { + SemiColon(); + } + + /* + * SemiColon + */ + final public void SemiColon() throws ParseException { + Assignment(); + label_2: while (true) { + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case SEMICOLON: + ; + break; + default: + jj_la1[2] = jj_gen; + break label_2; + } + jj_consume_token(SEMICOLON); + AstSemiColon jjtn001 = new AstSemiColon(JJTSEMICOLON); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + try { + Assignment(); + } catch (Throwable jjte001) { + if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte001; + } + } + } + if (jjte001 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte001; + } + } + } + { + if (true) { + throw (Error) jjte001; + } + } + } finally { + if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + } + } + } + } + + /* + * Assignment For '=', right associatve, then LambdaExpression or Choice or Assignment + */ + final public void Assignment() throws ParseException { + if (jj_2_1(3)) { + LambdaExpression(); + } else { + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case START_MAP: + case INTEGER_LITERAL: + case FLOATING_POINT_LITERAL: + case STRING_LITERAL: + case TRUE: + case FALSE: + case NULL: + case LPAREN: + case LBRACK: + case NOT0: + case NOT1: + case EMPTY: + case MINUS: + case IDENTIFIER: + Choice(); + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case ASSIGN: + jj_consume_token(ASSIGN); + AstAssign jjtn001 = new AstAssign(JJTASSIGN); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + try { + Assignment(); + } catch (Throwable jjte001) { + if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte001; + } + } + } + if (jjte001 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte001; + } + } + } + { + if (true) { + throw (Error) jjte001; + } + } + } finally { + if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + } + } + break; + default: + jj_la1[3] = jj_gen; + ; + } + break; + default: + jj_la1[4] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + } + + /* + * LambdaExpression + */ + final public void LambdaExpression() throws ParseException { + /* @bgen(jjtree) LambdaExpression */ + AstLambdaExpression jjtn000 = new AstLambdaExpression(JJTLAMBDAEXPRESSION); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + try { + LambdaParameters(); + jj_consume_token(ARROW); + if (jj_2_2(3)) { + LambdaExpression(); + } else { + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case START_MAP: + case INTEGER_LITERAL: + case FLOATING_POINT_LITERAL: + case STRING_LITERAL: + case TRUE: + case FALSE: + case NULL: + case LPAREN: + case LBRACK: + case NOT0: + case NOT1: + case EMPTY: + case MINUS: + case IDENTIFIER: + Choice(); + break; + default: + jj_la1[5] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte000; + } + } + } + if (jjte000 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte000; + } + } + } + { + if (true) { + throw (Error) jjte000; + } + } + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + + final public void LambdaParameters() throws ParseException { + /* @bgen(jjtree) LambdaParameters */ + AstLambdaParameters jjtn000 = new AstLambdaParameters(JJTLAMBDAPARAMETERS); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + try { + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case IDENTIFIER: + Identifier(); + break; + case LPAREN: + jj_consume_token(LPAREN); + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case IDENTIFIER: + Identifier(); + label_3: while (true) { + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case COMMA: + ; + break; + default: + jj_la1[6] = jj_gen; + break label_3; + } + jj_consume_token(COMMA); + Identifier(); + } + break; + default: + jj_la1[7] = jj_gen; + ; + } + jj_consume_token(RPAREN); + break; + default: + jj_la1[8] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte000; + } + } + } + if (jjte000 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte000; + } + } + } + { + if (true) { + throw (Error) jjte000; + } + } + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + + /* + * Choice For Choice markup a ? b : c, right associative + */ + final public void Choice() throws ParseException { + Or(); + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case QUESTIONMARK: + jj_consume_token(QUESTIONMARK); + Choice(); + jj_consume_token(COLON); + AstChoice jjtn001 = new AstChoice(JJTCHOICE); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + try { + Choice(); + } catch (Throwable jjte001) { + if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte001; + } + } + } + if (jjte001 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte001; + } + } + } + { + if (true) { + throw (Error) jjte001; + } + } + } finally { + if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 3); + } + } + break; + default: + jj_la1[9] = jj_gen; + ; + } + } + + /* + * Or For 'or' '||', then And + */ + final public void Or() throws ParseException { + And(); + label_4: while (true) { + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case OR0: + case OR1: + ; + break; + default: + jj_la1[10] = jj_gen; + break label_4; + } + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case OR0: + jj_consume_token(OR0); + break; + case OR1: + jj_consume_token(OR1); + break; + default: + jj_la1[11] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + AstOr jjtn001 = new AstOr(JJTOR); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + try { + And(); + } catch (Throwable jjte001) { + if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte001; + } + } + } + if (jjte001 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte001; + } + } + } + { + if (true) { + throw (Error) jjte001; + } + } + } finally { + if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + } + } + } + } + + /* + * And For 'and' '&&', then Equality + */ + final public void And() throws ParseException { + Equality(); + label_5: while (true) { + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case AND0: + case AND1: + ; + break; + default: + jj_la1[12] = jj_gen; + break label_5; + } + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case AND0: + jj_consume_token(AND0); + break; + case AND1: + jj_consume_token(AND1); + break; + default: + jj_la1[13] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + AstAnd jjtn001 = new AstAnd(JJTAND); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + try { + Equality(); + } catch (Throwable jjte001) { + if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte001; + } + } + } + if (jjte001 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte001; + } + } + } + { + if (true) { + throw (Error) jjte001; + } + } + } finally { + if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + } + } + } + } + + /* + * Equality For '==' 'eq' '!=' 'ne', then Compare + */ + final public void Equality() throws ParseException { + Compare(); + label_6: while (true) { + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case EQ0: + case EQ1: + case NE0: + case NE1: + ; + break; + default: + jj_la1[14] = jj_gen; + break label_6; + } + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case EQ0: + case EQ1: + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case EQ0: + jj_consume_token(EQ0); + break; + case EQ1: + jj_consume_token(EQ1); + break; + default: + jj_la1[15] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + AstEqual jjtn001 = new AstEqual(JJTEQUAL); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + try { + Compare(); + } catch (Throwable jjte001) { + if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte001; + } + } + } + if (jjte001 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte001; + } + } + } + { + if (true) { + throw (Error) jjte001; + } + } + } finally { + if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + } + } + break; + case NE0: + case NE1: + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case NE0: + jj_consume_token(NE0); + break; + case NE1: + jj_consume_token(NE1); + break; + default: + jj_la1[16] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + AstNotEqual jjtn002 = new AstNotEqual(JJTNOTEQUAL); + boolean jjtc002 = true; + jjtree.openNodeScope(jjtn002); + try { + Compare(); + } catch (Throwable jjte002) { + if (jjtc002) { + jjtree.clearNodeScope(jjtn002); + jjtc002 = false; + } else { + jjtree.popNode(); + } + if (jjte002 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte002; + } + } + } + if (jjte002 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte002; + } + } + } + { + if (true) { + throw (Error) jjte002; + } + } + } finally { + if (jjtc002) { + jjtree.closeNodeScope(jjtn002, 2); + } + } + break; + default: + jj_la1[17] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + } + + /* + * Compare For a bunch of them, then Math + */ + final public void Compare() throws ParseException { + Concatenation(); + label_7: while (true) { + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case GT0: + case GT1: + case LT0: + case LT1: + case GE0: + case GE1: + case LE0: + case LE1: + ; + break; + default: + jj_la1[18] = jj_gen; + break label_7; + } + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case LT0: + case LT1: + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case LT0: + jj_consume_token(LT0); + break; + case LT1: + jj_consume_token(LT1); + break; + default: + jj_la1[19] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + AstLessThan jjtn001 = new AstLessThan(JJTLESSTHAN); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + try { + Concatenation(); + } catch (Throwable jjte001) { + if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte001; + } + } + } + if (jjte001 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte001; + } + } + } + { + if (true) { + throw (Error) jjte001; + } + } + } finally { + if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + } + } + break; + case GT0: + case GT1: + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case GT0: + jj_consume_token(GT0); + break; + case GT1: + jj_consume_token(GT1); + break; + default: + jj_la1[20] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + AstGreaterThan jjtn002 = new AstGreaterThan(JJTGREATERTHAN); + boolean jjtc002 = true; + jjtree.openNodeScope(jjtn002); + try { + Concatenation(); + } catch (Throwable jjte002) { + if (jjtc002) { + jjtree.clearNodeScope(jjtn002); + jjtc002 = false; + } else { + jjtree.popNode(); + } + if (jjte002 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte002; + } + } + } + if (jjte002 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte002; + } + } + } + { + if (true) { + throw (Error) jjte002; + } + } + } finally { + if (jjtc002) { + jjtree.closeNodeScope(jjtn002, 2); + } + } + break; + case LE0: + case LE1: + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case LE0: + jj_consume_token(LE0); + break; + case LE1: + jj_consume_token(LE1); + break; + default: + jj_la1[21] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + AstLessThanEqual jjtn003 = new AstLessThanEqual(JJTLESSTHANEQUAL); + boolean jjtc003 = true; + jjtree.openNodeScope(jjtn003); + try { + Concatenation(); + } catch (Throwable jjte003) { + if (jjtc003) { + jjtree.clearNodeScope(jjtn003); + jjtc003 = false; + } else { + jjtree.popNode(); + } + if (jjte003 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte003; + } + } + } + if (jjte003 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte003; + } + } + } + { + if (true) { + throw (Error) jjte003; + } + } + } finally { + if (jjtc003) { + jjtree.closeNodeScope(jjtn003, 2); + } + } + break; + case GE0: + case GE1: + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case GE0: + jj_consume_token(GE0); + break; + case GE1: + jj_consume_token(GE1); + break; + default: + jj_la1[22] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + AstGreaterThanEqual jjtn004 = new AstGreaterThanEqual(JJTGREATERTHANEQUAL); + boolean jjtc004 = true; + jjtree.openNodeScope(jjtn004); + try { + Concatenation(); + } catch (Throwable jjte004) { + if (jjtc004) { + jjtree.clearNodeScope(jjtn004); + jjtc004 = false; + } else { + jjtree.popNode(); + } + if (jjte004 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte004; + } + } + } + if (jjte004 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte004; + } + } + } + { + if (true) { + throw (Error) jjte004; + } + } + } finally { + if (jjtc004) { + jjtree.closeNodeScope(jjtn004, 2); + } + } + break; + default: + jj_la1[23] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + } + + /* + * Concatenation For '&', then Math() + */ + final public void Concatenation() throws ParseException { + Math(); + label_8: while (true) { + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case CONCAT: + ; + break; + default: + jj_la1[24] = jj_gen; + break label_8; + } + jj_consume_token(CONCAT); + AstConcat jjtn001 = new AstConcat(JJTCONCAT); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + try { + Math(); + } catch (Throwable jjte001) { + if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte001; + } + } + } + if (jjte001 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte001; + } + } + } + { + if (true) { + throw (Error) jjte001; + } + } + } finally { + if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + } + } + } + } + + /* + * Math For '+' '-', then Multiplication + */ + final public void Math() throws ParseException { + Multiplication(); + label_9: while (true) { + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case PLUS: + case MINUS: + ; + break; + default: + jj_la1[25] = jj_gen; + break label_9; + } + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case PLUS: + jj_consume_token(PLUS); + AstPlus jjtn001 = new AstPlus(JJTPLUS); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + try { + Multiplication(); + } catch (Throwable jjte001) { + if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte001; + } + } + } + if (jjte001 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte001; + } + } + } + { + if (true) { + throw (Error) jjte001; + } + } + } finally { + if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + } + } + break; + case MINUS: + jj_consume_token(MINUS); + AstMinus jjtn002 = new AstMinus(JJTMINUS); + boolean jjtc002 = true; + jjtree.openNodeScope(jjtn002); + try { + Multiplication(); + } catch (Throwable jjte002) { + if (jjtc002) { + jjtree.clearNodeScope(jjtn002); + jjtc002 = false; + } else { + jjtree.popNode(); + } + if (jjte002 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte002; + } + } + } + if (jjte002 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte002; + } + } + } + { + if (true) { + throw (Error) jjte002; + } + } + } finally { + if (jjtc002) { + jjtree.closeNodeScope(jjtn002, 2); + } + } + break; + default: + jj_la1[26] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + } + + /* + * Multiplication For a bunch of them, then Unary + */ + final public void Multiplication() throws ParseException { + Unary(); + label_10: while (true) { + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case MULT: + case DIV0: + case DIV1: + case MOD0: + case MOD1: + ; + break; + default: + jj_la1[27] = jj_gen; + break label_10; + } + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case MULT: + jj_consume_token(MULT); + AstMult jjtn001 = new AstMult(JJTMULT); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + try { + Unary(); + } catch (Throwable jjte001) { + if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte001; + } + } + } + if (jjte001 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte001; + } + } + } + { + if (true) { + throw (Error) jjte001; + } + } + } finally { + if (jjtc001) { + jjtree.closeNodeScope(jjtn001, 2); + } + } + break; + case DIV0: + case DIV1: + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case DIV0: + jj_consume_token(DIV0); + break; + case DIV1: + jj_consume_token(DIV1); + break; + default: + jj_la1[28] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + AstDiv jjtn002 = new AstDiv(JJTDIV); + boolean jjtc002 = true; + jjtree.openNodeScope(jjtn002); + try { + Unary(); + } catch (Throwable jjte002) { + if (jjtc002) { + jjtree.clearNodeScope(jjtn002); + jjtc002 = false; + } else { + jjtree.popNode(); + } + if (jjte002 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte002; + } + } + } + if (jjte002 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte002; + } + } + } + { + if (true) { + throw (Error) jjte002; + } + } + } finally { + if (jjtc002) { + jjtree.closeNodeScope(jjtn002, 2); + } + } + break; + case MOD0: + case MOD1: + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case MOD0: + jj_consume_token(MOD0); + break; + case MOD1: + jj_consume_token(MOD1); + break; + default: + jj_la1[29] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + AstMod jjtn003 = new AstMod(JJTMOD); + boolean jjtc003 = true; + jjtree.openNodeScope(jjtn003); + try { + Unary(); + } catch (Throwable jjte003) { + if (jjtc003) { + jjtree.clearNodeScope(jjtn003); + jjtc003 = false; + } else { + jjtree.popNode(); + } + if (jjte003 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte003; + } + } + } + if (jjte003 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte003; + } + } + } + { + if (true) { + throw (Error) jjte003; + } + } + } finally { + if (jjtc003) { + jjtree.closeNodeScope(jjtn003, 2); + } + } + break; + default: + jj_la1[30] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + } + + /* + * Unary For '-' '!' 'not' 'empty', then Value + */ + final public void Unary() throws ParseException { + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case MINUS: + jj_consume_token(MINUS); + AstNegative jjtn001 = new AstNegative(JJTNEGATIVE); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + try { + Unary(); + } catch (Throwable jjte001) { + if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte001; + } + } + } + if (jjte001 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte001; + } + } + } + { + if (true) { + throw (Error) jjte001; + } + } + } finally { + if (jjtc001) { + jjtree.closeNodeScope(jjtn001, true); + } + } + break; + case NOT0: + case NOT1: + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case NOT0: + jj_consume_token(NOT0); + break; + case NOT1: + jj_consume_token(NOT1); + break; + default: + jj_la1[31] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + AstNot jjtn002 = new AstNot(JJTNOT); + boolean jjtc002 = true; + jjtree.openNodeScope(jjtn002); + try { + Unary(); + } catch (Throwable jjte002) { + if (jjtc002) { + jjtree.clearNodeScope(jjtn002); + jjtc002 = false; + } else { + jjtree.popNode(); + } + if (jjte002 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte002; + } + } + } + if (jjte002 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte002; + } + } + } + { + if (true) { + throw (Error) jjte002; + } + } + } finally { + if (jjtc002) { + jjtree.closeNodeScope(jjtn002, true); + } + } + break; + case EMPTY: + jj_consume_token(EMPTY); + AstEmpty jjtn003 = new AstEmpty(JJTEMPTY); + boolean jjtc003 = true; + jjtree.openNodeScope(jjtn003); + try { + Unary(); + } catch (Throwable jjte003) { + if (jjtc003) { + jjtree.clearNodeScope(jjtn003); + jjtc003 = false; + } else { + jjtree.popNode(); + } + if (jjte003 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte003; + } + } + } + if (jjte003 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte003; + } + } + } + { + if (true) { + throw (Error) jjte003; + } + } + } finally { + if (jjtc003) { + jjtree.closeNodeScope(jjtn003, true); + } + } + break; + case START_MAP: + case INTEGER_LITERAL: + case FLOATING_POINT_LITERAL: + case STRING_LITERAL: + case TRUE: + case FALSE: + case NULL: + case LPAREN: + case LBRACK: + case IDENTIFIER: + Value(); + break; + default: + jj_la1[32] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + + /* + * Value Defines Prefix plus zero or more Suffixes + */ + final public void Value() throws ParseException { + AstValue jjtn001 = new AstValue(JJTVALUE); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + try { + ValuePrefix(); + label_11: while (true) { + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case DOT: + case LBRACK: + ; + break; + default: + jj_la1[33] = jj_gen; + break label_11; + } + ValueSuffix(); + } + } catch (Throwable jjte001) { + if (jjtc001) { + jjtree.clearNodeScope(jjtn001); + jjtc001 = false; + } else { + jjtree.popNode(); + } + if (jjte001 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte001; + } + } + } + if (jjte001 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte001; + } + } + } + { + if (true) { + throw (Error) jjte001; + } + } + } finally { + if (jjtc001) { + jjtree.closeNodeScope(jjtn001, jjtree.nodeArity() > 1); + } + } + } + + /* + * ValuePrefix For Literals, Variables, and Functions + */ + final public void ValuePrefix() throws ParseException { + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case INTEGER_LITERAL: + case FLOATING_POINT_LITERAL: + case STRING_LITERAL: + case TRUE: + case FALSE: + case NULL: + Literal(); + break; + case START_MAP: + case LPAREN: + case LBRACK: + case IDENTIFIER: + NonLiteral(); + break; + default: + jj_la1[34] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + + /* + * ValueSuffix Either dot or bracket notation + */ + final public void ValueSuffix() throws ParseException { + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case DOT: + DotSuffix(); + break; + case LBRACK: + BracketSuffix(); + break; + default: + jj_la1[35] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + + /* + * DotSuffix Dot Property and Dot Method + */ + final public void DotSuffix() throws ParseException { + /* @bgen(jjtree) DotSuffix */ + AstDotSuffix jjtn000 = new AstDotSuffix(JJTDOTSUFFIX); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + Token t = null; + try { + jj_consume_token(DOT); + t = jj_consume_token(IDENTIFIER); + jjtn000.setImage(t.image); + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case LPAREN: + MethodArguments(); + break; + default: + jj_la1[36] = jj_gen; + ; + } + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte000; + } + } + } + if (jjte000 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte000; + } + } + } + { + if (true) { + throw (Error) jjte000; + } + } + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + + /* + * BracketSuffix Sub Expression Suffix + */ + final public void BracketSuffix() throws ParseException { + /* @bgen(jjtree) BracketSuffix */ + AstBracketSuffix jjtn000 = new AstBracketSuffix(JJTBRACKETSUFFIX); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + try { + jj_consume_token(LBRACK); + Expression(); + jj_consume_token(RBRACK); + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case LPAREN: + MethodArguments(); + break; + default: + jj_la1[37] = jj_gen; + ; + } + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte000; + } + } + } + if (jjte000 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte000; + } + } + } + { + if (true) { + throw (Error) jjte000; + } + } + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + + /* + * MethodArguments + */ + final public void MethodArguments() throws ParseException { + /* @bgen(jjtree) MethodArguments */ + AstMethodArguments jjtn000 = new AstMethodArguments(JJTMETHODARGUMENTS); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + try { + jj_consume_token(LPAREN); + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case START_MAP: + case INTEGER_LITERAL: + case FLOATING_POINT_LITERAL: + case STRING_LITERAL: + case TRUE: + case FALSE: + case NULL: + case LPAREN: + case LBRACK: + case NOT0: + case NOT1: + case EMPTY: + case MINUS: + case IDENTIFIER: + Expression(); + label_12: while (true) { + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case COMMA: + ; + break; + default: + jj_la1[38] = jj_gen; + break label_12; + } + jj_consume_token(COMMA); + Expression(); + } + break; + default: + jj_la1[39] = jj_gen; + ; + } + jj_consume_token(RPAREN); + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte000; + } + } + } + if (jjte000 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte000; + } + } + } + { + if (true) { + throw (Error) jjte000; + } + } + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + + /* + * Parenthesized Lambda Expression, with optional invokation + */ + final public void LambdaExpressionOrCall() throws ParseException { + /* @bgen(jjtree) LambdaExpression */ + AstLambdaExpression jjtn000 = new AstLambdaExpression(JJTLAMBDAEXPRESSION); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + try { + jj_consume_token(LPAREN); + LambdaParameters(); + jj_consume_token(ARROW); + if (jj_2_3(3)) { + LambdaExpression(); + } else { + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case START_MAP: + case INTEGER_LITERAL: + case FLOATING_POINT_LITERAL: + case STRING_LITERAL: + case TRUE: + case FALSE: + case NULL: + case LPAREN: + case LBRACK: + case NOT0: + case NOT1: + case EMPTY: + case MINUS: + case IDENTIFIER: + Choice(); + break; + default: + jj_la1[40] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + jj_consume_token(RPAREN); + label_13: while (true) { + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case LPAREN: + ; + break; + default: + jj_la1[41] = jj_gen; + break label_13; + } + MethodArguments(); + } + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte000; + } + } + } + if (jjte000 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte000; + } + } + } + { + if (true) { + throw (Error) jjte000; + } + } + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + + /* + * NonLiteral For Grouped Operations, Identifiers, and Functions + */ + final public void NonLiteral() throws ParseException { + if (jj_2_4(4)) { + LambdaExpressionOrCall(); + } else { + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case LPAREN: + jj_consume_token(LPAREN); + Expression(); + jj_consume_token(RPAREN); + break; + default: + jj_la1[42] = jj_gen; + if (jj_2_5(4)) { + Function(); + } else { + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case IDENTIFIER: + Identifier(); + break; + case START_MAP: + MapData(); + break; + case LBRACK: + ListData(); + break; + default: + jj_la1[43] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + } + } + } + + final public void MapData() throws ParseException { + /* @bgen(jjtree) MapData */ + AstMapData jjtn000 = new AstMapData(JJTMAPDATA); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + try { + jj_consume_token(START_MAP); + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case START_MAP: + case INTEGER_LITERAL: + case FLOATING_POINT_LITERAL: + case STRING_LITERAL: + case TRUE: + case FALSE: + case NULL: + case LPAREN: + case LBRACK: + case NOT0: + case NOT1: + case EMPTY: + case MINUS: + case IDENTIFIER: + MapEntry(); + label_14: while (true) { + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case COMMA: + ; + break; + default: + jj_la1[44] = jj_gen; + break label_14; + } + jj_consume_token(COMMA); + MapEntry(); + } + break; + default: + jj_la1[45] = jj_gen; + ; + } + jj_consume_token(RCURL); + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte000; + } + } + } + if (jjte000 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte000; + } + } + } + { + if (true) { + throw (Error) jjte000; + } + } + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + + final public void MapEntry() throws ParseException { + /* @bgen(jjtree) MapEntry */ + AstMapEntry jjtn000 = new AstMapEntry(JJTMAPENTRY); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + try { + Expression(); + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case COLON: + jj_consume_token(COLON); + Expression(); + break; + default: + jj_la1[46] = jj_gen; + ; + } + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte000; + } + } + } + if (jjte000 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte000; + } + } + } + { + if (true) { + throw (Error) jjte000; + } + } + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + + final public void ListData() throws ParseException { + /* @bgen(jjtree) ListData */ + AstListData jjtn000 = new AstListData(JJTLISTDATA); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + try { + jj_consume_token(LBRACK); + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case START_MAP: + case INTEGER_LITERAL: + case FLOATING_POINT_LITERAL: + case STRING_LITERAL: + case TRUE: + case FALSE: + case NULL: + case LPAREN: + case LBRACK: + case NOT0: + case NOT1: + case EMPTY: + case MINUS: + case IDENTIFIER: + Expression(); + label_15: while (true) { + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case COMMA: + ; + break; + default: + jj_la1[47] = jj_gen; + break label_15; + } + jj_consume_token(COMMA); + Expression(); + } + break; + default: + jj_la1[48] = jj_gen; + ; + } + jj_consume_token(RBRACK); + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte000; + } + } + } + if (jjte000 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte000; + } + } + } + { + if (true) { + throw (Error) jjte000; + } + } + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + + /* + * Identifier Java Language Identifier + */ + final public void Identifier() throws ParseException { + /* @bgen(jjtree) Identifier */ + AstIdentifier jjtn000 = new AstIdentifier(JJTIDENTIFIER); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + Token t = null; + try { + t = jj_consume_token(IDENTIFIER); + jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + jjtn000.setImage(t.image); + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + + /* + * Function Namespace:Name(a,b,c) + */ + final public void Function() throws ParseException { + /* @bgen(jjtree) Function */ + AstFunction jjtn000 = new AstFunction(JJTFUNCTION); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + Token t0 = null; + Token t1 = null; + try { + t0 = jj_consume_token(IDENTIFIER); + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case COLON: + jj_consume_token(COLON); + t1 = jj_consume_token(IDENTIFIER); + break; + default: + jj_la1[49] = jj_gen; + ; + } + if (t1 != null) { + jjtn000.setPrefix(t0.image); + jjtn000.setLocalName(t1.image); + } else { + jjtn000.setLocalName(t0.image); + } + label_16: while (true) { + MethodArguments(); + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case LPAREN: + ; + break; + default: + jj_la1[50] = jj_gen; + break label_16; + } + } + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + { + if (true) { + throw (RuntimeException) jjte000; + } + } + } + if (jjte000 instanceof ParseException) { + { + if (true) { + throw (ParseException) jjte000; + } + } + } + { + if (true) { + throw (Error) jjte000; + } + } + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + + /* + * Literal Reserved Keywords + */ + final public void Literal() throws ParseException { + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case TRUE: + case FALSE: + Boolean(); + break; + case FLOATING_POINT_LITERAL: + FloatingPoint(); + break; + case INTEGER_LITERAL: + Integer(); + break; + case STRING_LITERAL: + String(); + break; + case NULL: + Null(); + break; + default: + jj_la1[51] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + + /* + * Boolean For 'true' 'false' + */ + final public void Boolean() throws ParseException { + switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + case TRUE: + AstTrue jjtn001 = new AstTrue(JJTTRUE); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); + try { + jj_consume_token(TRUE); + } finally { + if (jjtc001) { + jjtree.closeNodeScope(jjtn001, true); + } + } + break; + case FALSE: + AstFalse jjtn002 = new AstFalse(JJTFALSE); + boolean jjtc002 = true; + jjtree.openNodeScope(jjtn002); + try { + jj_consume_token(FALSE); + } finally { + if (jjtc002) { + jjtree.closeNodeScope(jjtn002, true); + } + } + break; + default: + jj_la1[52] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + + /* + * FloatinPoint For Decimal and Floating Point Literals + */ + final public void FloatingPoint() throws ParseException { + /* @bgen(jjtree) FloatingPoint */ + AstFloatingPoint jjtn000 = new AstFloatingPoint(JJTFLOATINGPOINT); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + Token t = null; + try { + t = jj_consume_token(FLOATING_POINT_LITERAL); + jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + jjtn000.setImage(t.image); + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + + /* + * Integer For Simple Numeric Literals + */ + final public void Integer() throws ParseException { + /* @bgen(jjtree) Integer */ + AstInteger jjtn000 = new AstInteger(JJTINTEGER); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + Token t = null; + try { + t = jj_consume_token(INTEGER_LITERAL); + jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + jjtn000.setImage(t.image); + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + + /* + * String For Quoted Literals + */ + final public void String() throws ParseException { + /* @bgen(jjtree) String */ + AstString jjtn000 = new AstString(JJTSTRING); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + Token t = null; + try { + t = jj_consume_token(STRING_LITERAL); + jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + jjtn000.setImage(t.image); + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + + /* + * Null For 'null' + */ + final public void Null() throws ParseException { + /* @bgen(jjtree) Null */ + AstNull jjtn000 = new AstNull(JJTNULL); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + try { + jj_consume_token(NULL); + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + + private boolean jj_2_1(int xla) { + jj_la = xla; + jj_lastpos = jj_scanpos = token; + try { + return !jj_3_1(); + } catch (LookaheadSuccess ls) { + return true; + } finally { + jj_save(0, xla); + } + } + + private boolean jj_2_2(int xla) { + jj_la = xla; + jj_lastpos = jj_scanpos = token; + try { + return !jj_3_2(); + } catch (LookaheadSuccess ls) { + return true; + } finally { + jj_save(1, xla); + } + } + + private boolean jj_2_3(int xla) { + jj_la = xla; + jj_lastpos = jj_scanpos = token; + try { + return !jj_3_3(); + } catch (LookaheadSuccess ls) { + return true; + } finally { + jj_save(2, xla); + } + } + + private boolean jj_2_4(int xla) { + jj_la = xla; + jj_lastpos = jj_scanpos = token; + try { + return !jj_3_4(); + } catch (LookaheadSuccess ls) { + return true; + } finally { + jj_save(3, xla); + } + } + + private boolean jj_2_5(int xla) { + jj_la = xla; + jj_lastpos = jj_scanpos = token; + try { + return !jj_3_5(); + } catch (LookaheadSuccess ls) { + return true; + } finally { + jj_save(4, xla); + } + } + + private boolean jj_3R_89() { + if (jj_scan_token(LBRACK)) { + return true; + } + Token xsp; + xsp = jj_scanpos; + if (jj_3R_102()) { + jj_scanpos = xsp; + } + if (jj_scan_token(RBRACK)) { + return true; + } + return false; + } + + private boolean jj_3R_33() { + if (jj_scan_token(COMMA)) { + return true; + } + return false; + } + + private boolean jj_3R_31() { + if (jj_3R_34()) { + return true; + } + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_49()) { + jj_scanpos = xsp; + break; + } + } + return false; + } + + private boolean jj_3R_47() { + if (jj_scan_token(QUESTIONMARK)) { + return true; + } + return false; + } + + private boolean jj_3R_103() { + if (jj_3R_35()) { + return true; + } + return false; + } + + private boolean jj_3R_101() { + if (jj_3R_103()) { + return true; + } + return false; + } + + private boolean jj_3R_27() { + if (jj_3R_31()) { + return true; + } + Token xsp; + xsp = jj_scanpos; + if (jj_3R_47()) { + jj_scanpos = xsp; + } + return false; + } + + private boolean jj_3R_79() { + if (jj_3R_89()) { + return true; + } + return false; + } + + private boolean jj_3R_88() { + if (jj_scan_token(START_MAP)) { + return true; + } + Token xsp; + xsp = jj_scanpos; + if (jj_3R_101()) { + jj_scanpos = xsp; + } + if (jj_scan_token(RCURL)) { + return true; + } + return false; + } + + private boolean jj_3R_78() { + if (jj_3R_88()) { + return true; + } + return false; + } + + private boolean jj_3R_77() { + if (jj_3R_29()) { + return true; + } + return false; + } + + private boolean jj_3_5() { + if (jj_3R_19()) { + return true; + } + return false; + } + + private boolean jj_3R_30() { + if (jj_3R_29()) { + return true; + } + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_33()) { + jj_scanpos = xsp; + break; + } + } + return false; + } + + private boolean jj_3R_76() { + if (jj_scan_token(LPAREN)) { + return true; + } + if (jj_3R_35()) { + return true; + } + return false; + } + + private boolean jj_3R_36() { + if (jj_scan_token(COMMA)) { + return true; + } + return false; + } + + private boolean jj_3_4() { + if (jj_3R_18()) { + return true; + } + return false; + } + + private boolean jj_3R_69() { + Token xsp; + xsp = jj_scanpos; + if (jj_3_4()) { + jj_scanpos = xsp; + if (jj_3R_76()) { + jj_scanpos = xsp; + if (jj_3_5()) { + jj_scanpos = xsp; + if (jj_3R_77()) { + jj_scanpos = xsp; + if (jj_3R_78()) { + jj_scanpos = xsp; + if (jj_3R_79()) { + return true; + } + } + } + } + } + } + return false; + } + + private boolean jj_3R_26() { + if (jj_scan_token(LPAREN)) { + return true; + } + Token xsp; + xsp = jj_scanpos; + if (jj_3R_30()) { + jj_scanpos = xsp; + } + if (jj_scan_token(RPAREN)) { + return true; + } + return false; + } + + private boolean jj_3R_20() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_25()) { + jj_scanpos = xsp; + if (jj_3R_26()) { + return true; + } + } + return false; + } + + private boolean jj_3R_25() { + if (jj_3R_29()) { + return true; + } + return false; + } + + private boolean jj_3R_45() { + if (jj_scan_token(ASSIGN)) { + return true; + } + return false; + } + + private boolean jj_3_2() { + if (jj_3R_17()) { + return true; + } + return false; + } + + private boolean jj_3_3() { + if (jj_3R_17()) { + return true; + } + return false; + } + + private boolean jj_3R_17() { + if (jj_3R_20()) { + return true; + } + if (jj_scan_token(ARROW)) { + return true; + } + Token xsp; + xsp = jj_scanpos; + if (jj_3_2()) { + jj_scanpos = xsp; + if (jj_3R_21()) { + return true; + } + } + return false; + } + + private boolean jj_3R_32() { + if (jj_3R_35()) { + return true; + } + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_36()) { + jj_scanpos = xsp; + break; + } + } + return false; + } + + private boolean jj_3R_41() { + if (jj_scan_token(SEMICOLON)) { + return true; + } + return false; + } + + private boolean jj_3R_18() { + if (jj_scan_token(LPAREN)) { + return true; + } + if (jj_3R_20()) { + return true; + } + if (jj_scan_token(ARROW)) { + return true; + } + Token xsp; + xsp = jj_scanpos; + if (jj_3_3()) { + jj_scanpos = xsp; + if (jj_3R_22()) { + return true; + } + } + return false; + } + + private boolean jj_3R_43() { + if (jj_3R_27()) { + return true; + } + Token xsp; + xsp = jj_scanpos; + if (jj_3R_45()) { + jj_scanpos = xsp; + } + return false; + } + + private boolean jj_3R_40() { + Token xsp; + xsp = jj_scanpos; + if (jj_3_1()) { + jj_scanpos = xsp; + if (jj_3R_43()) { + return true; + } + } + return false; + } + + private boolean jj_3_1() { + if (jj_3R_17()) { + return true; + } + return false; + } + + private boolean jj_3R_28() { + if (jj_scan_token(LPAREN)) { + return true; + } + Token xsp; + xsp = jj_scanpos; + if (jj_3R_32()) { + jj_scanpos = xsp; + } + if (jj_scan_token(RPAREN)) { + return true; + } + return false; + } + + private boolean jj_3R_38() { + if (jj_3R_40()) { + return true; + } + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_41()) { + jj_scanpos = xsp; + break; + } + } + return false; + } + + private boolean jj_3R_100() { + if (jj_scan_token(LBRACK)) { + return true; + } + return false; + } + + private boolean jj_3R_35() { + if (jj_3R_38()) { + return true; + } + return false; + } + + private boolean jj_3R_98() { + if (jj_3R_100()) { + return true; + } + return false; + } + + private boolean jj_3R_99() { + if (jj_scan_token(DOT)) { + return true; + } + return false; + } + + private boolean jj_3R_97() { + if (jj_3R_99()) { + return true; + } + return false; + } + + private boolean jj_3R_96() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_97()) { + jj_scanpos = xsp; + if (jj_3R_98()) { + return true; + } + } + return false; + } + + private boolean jj_3R_95() { + if (jj_3R_96()) { + return true; + } + return false; + } + + private boolean jj_3R_62() { + if (jj_3R_69()) { + return true; + } + return false; + } + + private boolean jj_3R_57() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_61()) { + jj_scanpos = xsp; + if (jj_3R_62()) { + return true; + } + } + return false; + } + + private boolean jj_3R_61() { + if (jj_3R_68()) { + return true; + } + return false; + } + + private boolean jj_3R_55() { + if (jj_3R_57()) { + return true; + } + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_95()) { + jj_scanpos = xsp; + break; + } + } + return false; + } + + private boolean jj_3R_53() { + if (jj_3R_55()) { + return true; + } + return false; + } + + private boolean jj_3R_52() { + if (jj_scan_token(EMPTY)) { + return true; + } + if (jj_3R_48()) { + return true; + } + return false; + } + + private boolean jj_3R_51() { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(39)) { + jj_scanpos = xsp; + if (jj_scan_token(40)) { + return true; + } + } + if (jj_3R_48()) { + return true; + } + return false; + } + + private boolean jj_3R_87() { + if (jj_scan_token(NULL)) { + return true; + } + return false; + } + + private boolean jj_3R_48() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_50()) { + jj_scanpos = xsp; + if (jj_3R_51()) { + jj_scanpos = xsp; + if (jj_3R_52()) { + jj_scanpos = xsp; + if (jj_3R_53()) { + return true; + } + } + } + } + return false; + } + + private boolean jj_3R_50() { + if (jj_scan_token(MINUS)) { + return true; + } + if (jj_3R_48()) { + return true; + } + return false; + } + + private boolean jj_3R_86() { + if (jj_scan_token(STRING_LITERAL)) { + return true; + } + return false; + } + + private boolean jj_3R_92() { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(53)) { + jj_scanpos = xsp; + if (jj_scan_token(54)) { + return true; + } + } + return false; + } + + private boolean jj_3R_91() { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(51)) { + jj_scanpos = xsp; + if (jj_scan_token(52)) { + return true; + } + } + return false; + } + + private boolean jj_3R_90() { + if (jj_scan_token(MULT)) { + return true; + } + return false; + } + + private boolean jj_3R_80() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_90()) { + jj_scanpos = xsp; + if (jj_3R_91()) { + jj_scanpos = xsp; + if (jj_3R_92()) { + return true; + } + } + } + return false; + } + + private boolean jj_3R_85() { + if (jj_scan_token(INTEGER_LITERAL)) { + return true; + } + return false; + } + + private boolean jj_3R_46() { + if (jj_3R_48()) { + return true; + } + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_80()) { + jj_scanpos = xsp; + break; + } + } + return false; + } + + private boolean jj_3R_84() { + if (jj_scan_token(FLOATING_POINT_LITERAL)) { + return true; + } + return false; + } + + private boolean jj_3R_82() { + if (jj_scan_token(MINUS)) { + return true; + } + return false; + } + + private boolean jj_3R_70() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_81()) { + jj_scanpos = xsp; + if (jj_3R_82()) { + return true; + } + } + return false; + } + + private boolean jj_3R_81() { + if (jj_scan_token(PLUS)) { + return true; + } + return false; + } + + private boolean jj_3R_94() { + if (jj_scan_token(FALSE)) { + return true; + } + return false; + } + + private boolean jj_3R_93() { + if (jj_scan_token(TRUE)) { + return true; + } + return false; + } + + private boolean jj_3R_83() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_93()) { + jj_scanpos = xsp; + if (jj_3R_94()) { + return true; + } + } + return false; + } + + private boolean jj_3R_44() { + if (jj_3R_46()) { + return true; + } + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_70()) { + jj_scanpos = xsp; + break; + } + } + return false; + } + + private boolean jj_3R_63() { + if (jj_scan_token(CONCAT)) { + return true; + } + return false; + } + + private boolean jj_3R_75() { + if (jj_3R_87()) { + return true; + } + return false; + } + + private boolean jj_3R_74() { + if (jj_3R_86()) { + return true; + } + return false; + } + + private boolean jj_3R_73() { + if (jj_3R_85()) { + return true; + } + return false; + } + + private boolean jj_3R_72() { + if (jj_3R_84()) { + return true; + } + return false; + } + + private boolean jj_3R_42() { + if (jj_3R_44()) { + return true; + } + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_63()) { + jj_scanpos = xsp; + break; + } + } + return false; + } + + private boolean jj_3R_67() { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(31)) { + jj_scanpos = xsp; + if (jj_scan_token(32)) { + return true; + } + } + return false; + } + + private boolean jj_3R_68() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_71()) { + jj_scanpos = xsp; + if (jj_3R_72()) { + jj_scanpos = xsp; + if (jj_3R_73()) { + jj_scanpos = xsp; + if (jj_3R_74()) { + jj_scanpos = xsp; + if (jj_3R_75()) { + return true; + } + } + } + } + } + return false; + } + + private boolean jj_3R_71() { + if (jj_3R_83()) { + return true; + } + return false; + } + + private boolean jj_3R_66() { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(33)) { + jj_scanpos = xsp; + if (jj_scan_token(34)) { + return true; + } + } + return false; + } + + private boolean jj_3R_23() { + if (jj_scan_token(COLON)) { + return true; + } + if (jj_scan_token(IDENTIFIER)) { + return true; + } + return false; + } + + private boolean jj_3R_65() { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(27)) { + jj_scanpos = xsp; + if (jj_scan_token(28)) { + return true; + } + } + return false; + } + + private boolean jj_3R_58() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_64()) { + jj_scanpos = xsp; + if (jj_3R_65()) { + jj_scanpos = xsp; + if (jj_3R_66()) { + jj_scanpos = xsp; + if (jj_3R_67()) { + return true; + } + } + } + } + return false; + } + + private boolean jj_3R_64() { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(29)) { + jj_scanpos = xsp; + if (jj_scan_token(30)) { + return true; + } + } + return false; + } + + private boolean jj_3R_24() { + if (jj_3R_28()) { + return true; + } + return false; + } + + private boolean jj_3R_39() { + if (jj_3R_42()) { + return true; + } + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_58()) { + jj_scanpos = xsp; + break; + } + } + return false; + } + + private boolean jj_3R_60() { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(37)) { + jj_scanpos = xsp; + if (jj_scan_token(38)) { + return true; + } + } + return false; + } + + private boolean jj_3R_19() { + if (jj_scan_token(IDENTIFIER)) { + return true; + } + Token xsp; + xsp = jj_scanpos; + if (jj_3R_23()) { + jj_scanpos = xsp; + } + if (jj_3R_24()) { + return true; + } + while (true) { + xsp = jj_scanpos; + if (jj_3R_24()) { + jj_scanpos = xsp; + break; + } + } + return false; + } + + private boolean jj_3R_56() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_59()) { + jj_scanpos = xsp; + if (jj_3R_60()) { + return true; + } + } + return false; + } + + private boolean jj_3R_59() { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(35)) { + jj_scanpos = xsp; + if (jj_scan_token(36)) { + return true; + } + } + return false; + } + + private boolean jj_3R_54() { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(41)) { + jj_scanpos = xsp; + if (jj_scan_token(42)) { + return true; + } + } + return false; + } + + private boolean jj_3R_37() { + if (jj_3R_39()) { + return true; + } + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_56()) { + jj_scanpos = xsp; + break; + } + } + return false; + } + + private boolean jj_3R_29() { + if (jj_scan_token(IDENTIFIER)) { + return true; + } + return false; + } + + private boolean jj_3R_34() { + if (jj_3R_37()) { + return true; + } + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_54()) { + jj_scanpos = xsp; + break; + } + } + return false; + } + + private boolean jj_3R_102() { + if (jj_3R_35()) { + return true; + } + return false; + } + + private boolean jj_3R_21() { + if (jj_3R_27()) { + return true; + } + return false; + } + + private boolean jj_3R_49() { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(43)) { + jj_scanpos = xsp; + if (jj_scan_token(44)) { + return true; + } + } + return false; + } + + private boolean jj_3R_22() { + if (jj_3R_27()) { + return true; + } + return false; + } + + /** Generated Token Manager. */ + public ELParserTokenManager token_source; + SimpleCharStream jj_input_stream; + /** Current token. */ + public Token token; + /** Next token. */ + public Token jj_nt; + private int jj_ntk; + private Token jj_scanpos, jj_lastpos; + private int jj_la; + private int jj_gen; + final private int[] jj_la1 = new int[53]; + static private int[] jj_la1_0; + static private int[] jj_la1_1; + static { + jj_la1_init_0(); + jj_la1_init_1(); + } + + private static void jj_la1_init_0() { + jj_la1_0 = new int[] { 0xe, 0xe, 0x4000000, 0x0, 0x575a00, 0x575a00, 0x2000000, 0x0, 0x100000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf8000000, + 0x60000000, 0x18000000, 0x0, 0x80000000, 0xf8000000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x575a00, 0x480000, 0x575a00, 0x480000, 0x100000, + 0x100000, 0x2000000, 0x575a00, 0x575a00, 0x100000, 0x100000, 0x400200, 0x2000000, 0x575a00, 0x1000000, 0x2000000, 0x575a00, 0x1000000, 0x100000, + 0x75800, 0x30000, }; + } + + private static void jj_la1_init_1() { + jj_la1_1 = new int[] { 0x0, 0x0, 0x0, 0x1000000, 0x4022180, 0x4022180, 0x0, 0x4000000, 0x4000000, 0x40000, 0x1800, 0x1800, 0x600, 0x600, 0x78, 0x18, + 0x60, 0x78, 0x7, 0x0, 0x0, 0x6, 0x1, 0x7, 0x800000, 0x30000, 0x30000, 0x788000, 0x180000, 0x600000, 0x788000, 0x180, 0x4022180, 0x0, 0x4000000, + 0x0, 0x0, 0x0, 0x0, 0x4022180, 0x4022180, 0x0, 0x0, 0x4000000, 0x0, 0x4022180, 0x0, 0x0, 0x4022180, 0x0, 0x0, 0x0, 0x0, }; + } + + final private JJCalls[] jj_2_rtns = new JJCalls[5]; + private boolean jj_rescan = false; + private int jj_gc = 0; + + /** Constructor with InputStream. */ + public ELParser(java.io.InputStream stream) { + this(stream, null); + } + + /** Constructor with InputStream and supplied encoding */ + public ELParser(java.io.InputStream stream, String encoding) { + try { + jj_input_stream = new SimpleCharStream(stream, encoding, 1, 1); + } catch (java.io.UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + token_source = new ELParserTokenManager(jj_input_stream); + token = new Token(); + jj_ntk = -1; + jj_gen = 0; + for (int i = 0; i < 53; i++) { + jj_la1[i] = -1; + } + for (int i = 0; i < jj_2_rtns.length; i++) { + jj_2_rtns[i] = new JJCalls(); + } + } + + /** Reinitialise. */ + public void ReInit(java.io.InputStream stream) { + ReInit(stream, null); + } + + /** Reinitialise. */ + public void ReInit(java.io.InputStream stream, String encoding) { + try { + jj_input_stream.ReInit(stream, encoding, 1, 1); + } catch (java.io.UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + token_source.ReInit(jj_input_stream); + token = new Token(); + jj_ntk = -1; + jjtree.reset(); + jj_gen = 0; + for (int i = 0; i < 53; i++) { + jj_la1[i] = -1; + } + for (int i = 0; i < jj_2_rtns.length; i++) { + jj_2_rtns[i] = new JJCalls(); + } + } + + /** Constructor. */ + public ELParser(java.io.Reader stream) { + jj_input_stream = new SimpleCharStream(stream, 1, 1); + token_source = new ELParserTokenManager(jj_input_stream); + token = new Token(); + jj_ntk = -1; + jj_gen = 0; + for (int i = 0; i < 53; i++) { + jj_la1[i] = -1; + } + for (int i = 0; i < jj_2_rtns.length; i++) { + jj_2_rtns[i] = new JJCalls(); + } + } + + /** Reinitialise. */ + public void ReInit(java.io.Reader stream) { + jj_input_stream.ReInit(stream, 1, 1); + token_source.ReInit(jj_input_stream); + token = new Token(); + jj_ntk = -1; + jjtree.reset(); + jj_gen = 0; + for (int i = 0; i < 53; i++) { + jj_la1[i] = -1; + } + for (int i = 0; i < jj_2_rtns.length; i++) { + jj_2_rtns[i] = new JJCalls(); + } + } + + /** Constructor with generated Token Manager. */ + public ELParser(ELParserTokenManager tm) { + token_source = tm; + token = new Token(); + jj_ntk = -1; + jj_gen = 0; + for (int i = 0; i < 53; i++) { + jj_la1[i] = -1; + } + for (int i = 0; i < jj_2_rtns.length; i++) { + jj_2_rtns[i] = new JJCalls(); + } + } + + /** Reinitialise. */ + public void ReInit(ELParserTokenManager tm) { + token_source = tm; + token = new Token(); + jj_ntk = -1; + jjtree.reset(); + jj_gen = 0; + for (int i = 0; i < 53; i++) { + jj_la1[i] = -1; + } + for (int i = 0; i < jj_2_rtns.length; i++) { + jj_2_rtns[i] = new JJCalls(); + } + } + + private Token jj_consume_token(int kind) throws ParseException { + Token oldToken; + if ((oldToken = token).next != null) { + token = token.next; + } else { + token = token.next = token_source.getNextToken(); + } + jj_ntk = -1; + if (token.kind == kind) { + jj_gen++; + if (++jj_gc > 100) { + jj_gc = 0; + for (int i = 0; i < jj_2_rtns.length; i++) { + JJCalls c = jj_2_rtns[i]; + while (c != null) { + if (c.gen < jj_gen) { + c.first = null; + } + c = c.next; + } + } + } + return token; + } + token = oldToken; + jj_kind = kind; + throw generateParseException(); + } + + static private final class LookaheadSuccess extends java.lang.Error { + } + + static final private LookaheadSuccess jj_ls = new LookaheadSuccess(); + + private boolean jj_scan_token(int kind) { + if (jj_scanpos == jj_lastpos) { + jj_la--; + if (jj_scanpos.next == null) { + jj_lastpos = jj_scanpos = jj_scanpos.next = token_source.getNextToken(); + } else { + jj_lastpos = jj_scanpos = jj_scanpos.next; + } + } else { + jj_scanpos = jj_scanpos.next; + } + if (jj_rescan) { + int i = 0; + Token tok = token; + while (tok != null && tok != jj_scanpos) { + i++; + tok = tok.next; + } + if (tok != null) { + jj_add_error_token(kind, i); + } + } + if (jj_scanpos.kind != kind) { + return true; + } + if (jj_la == 0 && jj_scanpos == jj_lastpos) { + throw jj_ls; + } + return false; + } + + /** Get the next Token. */ + final public Token getNextToken() { + if (token.next != null) { + token = token.next; + } else { + token = token.next = token_source.getNextToken(); + } + jj_ntk = -1; + jj_gen++; + return token; + } + + /** Get the specific Token. */ + final public Token getToken(int index) { + Token t = token; + for (int i = 0; i < index; i++) { + if (t.next != null) { + t = t.next; + } else { + t = t.next = token_source.getNextToken(); + } + } + return t; + } + + private int jj_ntk() { + if ((jj_nt = token.next) == null) { + return (jj_ntk = (token.next = token_source.getNextToken()).kind); + } else { + return (jj_ntk = jj_nt.kind); + } + } + + private java.util.List<int[]> jj_expentries = new java.util.ArrayList<int[]>(); + private int[] jj_expentry; + private int jj_kind = -1; + private int[] jj_lasttokens = new int[100]; + private int jj_endpos; + + private void jj_add_error_token(int kind, int pos) { + if (pos >= 100) { + return; + } + if (pos == jj_endpos + 1) { + jj_lasttokens[jj_endpos++] = kind; + } else if (jj_endpos != 0) { + jj_expentry = new int[jj_endpos]; + for (int i = 0; i < jj_endpos; i++) { + jj_expentry[i] = jj_lasttokens[i]; + } + jj_entries_loop: for (java.util.Iterator<?> it = jj_expentries.iterator(); it.hasNext();) { + int[] oldentry = (int[]) (it.next()); + if (oldentry.length == jj_expentry.length) { + for (int i = 0; i < jj_expentry.length; i++) { + if (oldentry[i] != jj_expentry[i]) { + continue jj_entries_loop; + } + } + jj_expentries.add(jj_expentry); + break jj_entries_loop; + } + } + if (pos != 0) { + jj_lasttokens[(jj_endpos = pos) - 1] = kind; + } + } + } + + /** Generate ParseException. */ + public ParseException generateParseException() { + jj_expentries.clear(); + boolean[] la1tokens = new boolean[63]; + if (jj_kind >= 0) { + la1tokens[jj_kind] = true; + jj_kind = -1; + } + for (int i = 0; i < 53; i++) { + if (jj_la1[i] == jj_gen) { + for (int j = 0; j < 32; j++) { + if ((jj_la1_0[i] & (1 << j)) != 0) { + la1tokens[j] = true; + } + if ((jj_la1_1[i] & (1 << j)) != 0) { + la1tokens[32 + j] = true; + } + } + } + } + for (int i = 0; i < 63; i++) { + if (la1tokens[i]) { + jj_expentry = new int[1]; + jj_expentry[0] = i; + jj_expentries.add(jj_expentry); + } + } + jj_endpos = 0; + jj_rescan_token(); + jj_add_error_token(0, 0); + int[][] exptokseq = new int[jj_expentries.size()][]; + for (int i = 0; i < jj_expentries.size(); i++) { + exptokseq[i] = jj_expentries.get(i); + } + return new ParseException(token, exptokseq, tokenImage); + } + + /** Enable tracing. */ + final public void enable_tracing() { + } + + /** Disable tracing. */ + final public void disable_tracing() { + } + + private void jj_rescan_token() { + jj_rescan = true; + for (int i = 0; i < 5; i++) { + try { + JJCalls p = jj_2_rtns[i]; + do { + if (p.gen > jj_gen) { + jj_la = p.arg; + jj_lastpos = jj_scanpos = p.first; + switch (i) { + case 0: + jj_3_1(); + break; + case 1: + jj_3_2(); + break; + case 2: + jj_3_3(); + break; + case 3: + jj_3_4(); + break; + case 4: + jj_3_5(); + break; + } + } + p = p.next; + } while (p != null); + } catch (LookaheadSuccess ls) { + } + } + jj_rescan = false; + } + + private void jj_save(int index, int xla) { + JJCalls p = jj_2_rtns[index]; + while (p.gen > jj_gen) { + if (p.next == null) { + p = p.next = new JJCalls(); + break; + } + p = p.next; + } + p.gen = jj_gen + xla - jj_la; + p.first = token; + p.arg = xla; + } + + static final class JJCalls { + int gen; + Token first; + int arg; + JJCalls next; + } + +}
diff --git a/impl/src/main/java/com/sun/el/parser/ELParser.jjt b/impl/src/main/java/com/sun/el/parser/ELParser.jjt new file mode 100644 index 0000000..4cccc57 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/ELParser.jjt
@@ -0,0 +1,573 @@ +/* + * Copyright (c) 1997, 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +/* + Author: Jacob Hookom + Email: jacob at hookom.net + + Author: Kin-man Chung (EL 2.2 and EL 3.0) +*/ + +/* == Option Declaration == */ +options +{ + STATIC=false; + NODE_PREFIX="Ast"; + VISITOR_EXCEPTION="javax.el.ELException"; + VISITOR=false; + MULTI=true; + NODE_DEFAULT_VOID=true; + JAVA_UNICODE_ESCAPE=false; + UNICODE_INPUT=true; + BUILD_NODE_FILES=true; +} + +/* == Parser Declaration == */ +PARSER_BEGIN( ELParser ) +package com.sun.el.parser; +import java.io.StringReader; +import javax.el.ELException; +public class ELParser +{ + public static Node parse(String ref) throws ELException + { + try { + return (new ELParser(new StringReader(ref))).CompositeExpression(); + } catch (ParseException pe) { + throw new ELException(pe.getMessage()); + } + } +} +PARSER_END( ELParser ) + +/* + * CompositeExpression + * Allow most flexible parsing, restrict by examining + * type of returned node + */ +AstCompositeExpression CompositeExpression() #CompositeExpression : {} +{ + (DeferredExpression() | DynamicExpression() | LiteralExpression())* <EOF> { return jjtThis; } +} + +/* + * LiteralExpression + * Non-EL Expression blocks + */ +void LiteralExpression() #LiteralExpression : { Token t = null; } +{ + t=<LITERAL_EXPRESSION> { jjtThis.setImage(t.image); } +} + +/* + * DeferredExpression + * #{..} Expressions + */ +void DeferredExpression() #DeferredExpression : {} +{ + <START_DEFERRED_EXPRESSION> Expression() <RCURL> +} + +/* + * DynamicExpression + * ${..} Expressions + */ +void DynamicExpression() #DynamicExpression : {} +{ + <START_DYNAMIC_EXPRESSION> Expression() <RCURL> +} + +/* + * Expression + * EL Expression Language Root + */ +void Expression() : {} +{ + SemiColon() +} + +/* + * SemiColon + */ +void SemiColon() : {} +{ + Assignment() (<SEMICOLON> Assignment() #SemiColon(2) )* +} + +/* + * Assignment + * For '=', right associatve, then LambdaExpression or Choice or Assignment + */ +void Assignment() : {} +{ + LOOKAHEAD(3) LambdaExpression() | + Choice() (<ASSIGN> Assignment() #Assign(2) )? +} + +/* + * LambdaExpression + */ +void LambdaExpression() #LambdaExpression : {} +{ + LambdaParameters() <ARROW> + (LOOKAHEAD(3) LambdaExpression() | Choice() ) +} + +void LambdaParameters() #LambdaParameters: {} +{ + Identifier() + | <LPAREN> + (Identifier() (<COMMA> Identifier())*)? + <RPAREN> +} + +/* + * Choice + * For Choice markup a ? b : c, right associative + */ +void Choice() : {} +{ + Or() (<QUESTIONMARK> Choice() <COLON> Choice() #Choice(3))? +} + +/* + * Or + * For 'or' '||', then And + */ +void Or() : {} +{ + And() ((<OR0>|<OR1>) And() #Or(2))* +} + +/* + * And + * For 'and' '&&', then Equality + */ +void And() : {} +{ + Equality() ((<AND0>|<AND1>) Equality() #And(2))* +} + +/* + * Equality + * For '==' 'eq' '!=' 'ne', then Compare + */ +void Equality() : {} +{ + Compare() + ( + ((<EQ0>|<EQ1>) Compare() #Equal(2)) + | + ((<NE0>|<NE1>) Compare() #NotEqual(2)) + )* +} + +/* + * Compare + * For a bunch of them, then Math + */ +void Compare() : {} +{ + Concatenation() + ( + ((<LT0>|<LT1>) Concatenation() #LessThan(2)) + | + ((<GT0>|<GT1>) Concatenation() #GreaterThan(2)) + | + ((<LE0>|<LE1>) Concatenation() #LessThanEqual(2)) + | + ((<GE0>|<GE1>) Concatenation() #GreaterThanEqual(2)) + )* +} + +/* + * Concatenation + * For '&', then Math() + */ +void Concatenation() : {} +{ + Math() ( <CONCAT> Math() #Concat(2) )* +} + +/* + * Math + * For '+' '-', then Multiplication + */ +void Math() : {} +{ + Multiplication() + ( + (<PLUS> Multiplication() #Plus(2)) + | + (<MINUS> Multiplication() #Minus(2)) + )* +} + +/* + * Multiplication + * For a bunch of them, then Unary + */ +void Multiplication() : {} +{ + Unary() + ( + (<MULT> Unary() #Mult(2)) + | + ((<DIV0>|<DIV1>) Unary() #Div(2)) + | + ((<MOD0>|<MOD1>) Unary() #Mod(2)) + )* +} + +/* + * Unary + * For '-' '!' 'not' 'empty', then Value + */ +void Unary() : {} +{ + <MINUS> Unary() #Negative + | + (<NOT0>|<NOT1>) Unary() #Not + | + <EMPTY> Unary() #Empty + | + Value() +} + +/* + * Value + * Defines Prefix plus zero or more Suffixes + */ +void Value() : {} +{ + (ValuePrefix() (ValueSuffix())*) #Value(>1) +} + +/* + * ValuePrefix + * For Literals, Variables, and Functions + */ +void ValuePrefix() : {} +{ + Literal() + | NonLiteral() +} + +/* + * ValueSuffix + * Either dot or bracket notation + */ +void ValueSuffix() : {} +{ + DotSuffix() | BracketSuffix() +} + +/* + * DotSuffix + * Dot Property and Dot Method + */ +void DotSuffix() #DotSuffix : { Token t = null; } +{ + <DOT> t=<IDENTIFIER> { jjtThis.setImage(t.image); } + (MethodArguments())? +} + +/* + * BracketSuffix + * Sub Expression Suffix + */ +void BracketSuffix() #BracketSuffix : {} +{ + <LBRACK> Expression() <RBRACK> + (MethodArguments())? +} + + +/* + * MethodArguments + */ +void MethodArguments() #MethodArguments : {} +{ + <LPAREN> (Expression() (<COMMA> Expression())*)? <RPAREN> +} + +/* + * Parenthesized Lambda Expression, with optional invokation + */ +void LambdaExpressionOrCall() #LambdaExpression : {} + +{ + <LPAREN> + LambdaParameters() <ARROW> + (LOOKAHEAD(3) LambdaExpression() | Choice() ) + <RPAREN> + (MethodArguments())* +} + +/* + * NonLiteral + * For Grouped Operations, Identifiers, and Functions + */ +void NonLiteral() : {} +{ + LOOKAHEAD(4) LambdaExpressionOrCall() + | <LPAREN> Expression() <RPAREN> + | LOOKAHEAD(4) Function() + | Identifier() + | MapData() + | ListData() +} + +void MapData() #MapData: {} +{ + <START_MAP> + ( MapEntry() ( <COMMA> MapEntry() )* )? + <RCURL> +} + +void MapEntry() #MapEntry: {} +{ + Expression() (<COLON> Expression())? +} + +void ListData() #ListData: {} +{ + <LBRACK> + ( Expression() ( <COMMA> Expression() )* )? + <RBRACK> +} + +/* + * Identifier + * Java Language Identifier + */ +void Identifier() #Identifier : { Token t = null; } +{ + t=<IDENTIFIER> { jjtThis.setImage(t.image); } +} + +/* + * Function + * Namespace:Name(a,b,c) + */ +void Function() #Function : +{ + Token t0 = null; + Token t1 = null; +} +{ + t0=<IDENTIFIER> (<COLON> t1=<IDENTIFIER>)? + { + if (t1 != null) { + jjtThis.setPrefix(t0.image); + jjtThis.setLocalName(t1.image); + } else { + jjtThis.setLocalName(t0.image); + } + } + (MethodArguments())+ +} + + +/* + * Literal + * Reserved Keywords + */ +void Literal() : {} +{ + Boolean() + | FloatingPoint() + | Integer() + | String() + | Null() +} + +/* + * Boolean + * For 'true' 'false' + */ +void Boolean() : {} +{ + <TRUE> #True + | <FALSE> #False +} + +/* + * FloatinPoint + * For Decimal and Floating Point Literals + */ +void FloatingPoint() #FloatingPoint : { Token t = null; } +{ + t=<FLOATING_POINT_LITERAL> { jjtThis.setImage(t.image); } +} + +/* + * Integer + * For Simple Numeric Literals + */ +void Integer() #Integer : { Token t = null; } +{ + t=<INTEGER_LITERAL> { jjtThis.setImage(t.image); } +} + +/* + * String + * For Quoted Literals + */ +void String() #String : { Token t = null; } +{ + t=<STRING_LITERAL> { jjtThis.setImage(t.image); } +} + +/* + * Null + * For 'null' + */ +void Null() #Null : {} +{ + <NULL> +} + + +/* ========================================================================== */ +TOKEN_MGR_DECLS: +{ + java.util.Stack<Integer> stack = new java.util.Stack<Integer>(); +} + +<DEFAULT> TOKEN : +{ + < LITERAL_EXPRESSION: + ((~["\\", "$", "#"]) + | ("\\" ("\\" | "$" | "#")) + | ("$" ~["{", "$", "#", "\\"]) + | ("#" ~["{", "$", "#", "\\"]) + )+ + | "$" + | "#" + > +| + < START_DYNAMIC_EXPRESSION: "${" > {stack.push(DEFAULT);}: IN_EXPRESSION +| + < START_DEFERRED_EXPRESSION: "#{" > {stack.push(DEFAULT);}: IN_EXPRESSION +} + +<DEFAULT> SKIP : { "\\" } + +<IN_EXPRESSION, IN_MAP> SKIP: +{ " " | "\t" | "\n" | "\r" } + +<IN_EXPRESSION, IN_MAP> TOKEN : +{ + < START_MAP : "{" > {stack.push(curLexState);}: IN_MAP +| < RCURL: "}" > {SwitchTo(stack.pop());} +| < INTEGER_LITERAL: ["0"-"9"] (["0"-"9"])* > +| < FLOATING_POINT_LITERAL: (["0"-"9"])+ "." (["0"-"9"])* (<EXPONENT>)? + | "." (["0"-"9"])+ (<EXPONENT>)? + | (["0"-"9"])+ <EXPONENT> + > +| < #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ > +| < STRING_LITERAL: ("\"" ((~["\"","\\"]) + | ("\\" ( ["\\","\""] )))* "\"") + | ("\'" ((~["\'","\\"]) + | ("\\" ( ["\\","\'"] )))* "\'") + > +| < BADLY_ESCAPED_STRING_LITERAL: ("\"" (~["\"","\\"])* ("\\" ( ~["\\","\""] ))) + | ("\'" (~["\'","\\"])* ("\\" ( ~["\\","\'"] ))) + > +| < TRUE : "true" > +| < FALSE : "false" > +| < NULL : "null" > +| < DOT : "." > +| < LPAREN : "(" > +| < RPAREN : ")" > +| < LBRACK : "[" > +| < RBRACK : "]" > +| < COLON : ":" > +| < COMMA : "," > +| < SEMICOLON : ";" > +| < GT0 : ">" > +| < GT1 : "gt" > +| < LT0 : "<" > +| < LT1 : "lt" > +| < GE0 : ">=" > +| < GE1 : "ge" > +| < LE0 : "<=" > +| < LE1 : "le" > +| < EQ0 : "==" > +| < EQ1 : "eq" > +| < NE0 : "!=" > +| < NE1 : "ne" > +| < NOT0 : "!" > +| < NOT1 : "not" > +| < AND0 : "&&" > +| < AND1 : "and" > +| < OR0 : "||" > +| < OR1 : "or" > +| < EMPTY : "empty" > +| < INSTANCEOF : "instanceof" > +| < MULT : "*" > +| < PLUS : "+" > +| < MINUS : "-" > +| < QUESTIONMARK : "?" > +| < DIV0 : "/" > +| < DIV1 : "div" > +| < MOD0 : "%" > +| < MOD1 : "mod" > +| < CONCAT : "+=" > +| < ASSIGN : "=" > +| < ARROW : "->" > +| < IDENTIFIER : (<LETTER>|<IMPL_OBJ_START>) (<LETTER>|<DIGIT>)* > +| < #IMPL_OBJ_START: "#" > +| < #LETTER: + [ + "\u0024", + "\u0041"-"\u005a", + "\u005f", + "\u0061"-"\u007a", + "\u00c0"-"\u00d6", + "\u00d8"-"\u00f6", + "\u00f8"-"\u00ff", + "\u0100"-"\u1fff", + "\u3040"-"\u318f", + "\u3300"-"\u337f", + "\u3400"-"\u3d2d", + "\u4e00"-"\u9fff", + "\uf900"-"\ufaff" + ] + > +| < #DIGIT: + [ + "\u0030"-"\u0039", + "\u0660"-"\u0669", + "\u06f0"-"\u06f9", + "\u0966"-"\u096f", + "\u09e6"-"\u09ef", + "\u0a66"-"\u0a6f", + "\u0ae6"-"\u0aef", + "\u0b66"-"\u0b6f", + "\u0be7"-"\u0bef", + "\u0c66"-"\u0c6f", + "\u0ce6"-"\u0cef", + "\u0d66"-"\u0d6f", + "\u0e50"-"\u0e59", + "\u0ed0"-"\u0ed9", + "\u1040"-"\u1049" + ] + > +| < ILLEGAL_CHARACTER: (~[]) > +} +
diff --git a/impl/src/main/java/com/sun/el/parser/ELParserConstants.java b/impl/src/main/java/com/sun/el/parser/ELParserConstants.java new file mode 100644 index 0000000..41445ce --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/ELParserConstants.java
@@ -0,0 +1,157 @@ +/* + * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +/* Generated By:JJTree&JavaCC: Do not edit this line. ELParserConstants.java */ +package com.sun.el.parser; + +/** + * Token literal values and constants. Generated by org.javacc.parser.OtherFilesGen#start() + */ +public interface ELParserConstants { + + /** End of File. */ + int EOF = 0; + /** RegularExpression Id. */ + int LITERAL_EXPRESSION = 1; + /** RegularExpression Id. */ + int START_DYNAMIC_EXPRESSION = 2; + /** RegularExpression Id. */ + int START_DEFERRED_EXPRESSION = 3; + /** RegularExpression Id. */ + int START_MAP = 9; + /** RegularExpression Id. */ + int RCURL = 10; + /** RegularExpression Id. */ + int INTEGER_LITERAL = 11; + /** RegularExpression Id. */ + int FLOATING_POINT_LITERAL = 12; + /** RegularExpression Id. */ + int EXPONENT = 13; + /** RegularExpression Id. */ + int STRING_LITERAL = 14; + /** RegularExpression Id. */ + int BADLY_ESCAPED_STRING_LITERAL = 15; + /** RegularExpression Id. */ + int TRUE = 16; + /** RegularExpression Id. */ + int FALSE = 17; + /** RegularExpression Id. */ + int NULL = 18; + /** RegularExpression Id. */ + int DOT = 19; + /** RegularExpression Id. */ + int LPAREN = 20; + /** RegularExpression Id. */ + int RPAREN = 21; + /** RegularExpression Id. */ + int LBRACK = 22; + /** RegularExpression Id. */ + int RBRACK = 23; + /** RegularExpression Id. */ + int COLON = 24; + /** RegularExpression Id. */ + int COMMA = 25; + /** RegularExpression Id. */ + int SEMICOLON = 26; + /** RegularExpression Id. */ + int GT0 = 27; + /** RegularExpression Id. */ + int GT1 = 28; + /** RegularExpression Id. */ + int LT0 = 29; + /** RegularExpression Id. */ + int LT1 = 30; + /** RegularExpression Id. */ + int GE0 = 31; + /** RegularExpression Id. */ + int GE1 = 32; + /** RegularExpression Id. */ + int LE0 = 33; + /** RegularExpression Id. */ + int LE1 = 34; + /** RegularExpression Id. */ + int EQ0 = 35; + /** RegularExpression Id. */ + int EQ1 = 36; + /** RegularExpression Id. */ + int NE0 = 37; + /** RegularExpression Id. */ + int NE1 = 38; + /** RegularExpression Id. */ + int NOT0 = 39; + /** RegularExpression Id. */ + int NOT1 = 40; + /** RegularExpression Id. */ + int AND0 = 41; + /** RegularExpression Id. */ + int AND1 = 42; + /** RegularExpression Id. */ + int OR0 = 43; + /** RegularExpression Id. */ + int OR1 = 44; + /** RegularExpression Id. */ + int EMPTY = 45; + /** RegularExpression Id. */ + int INSTANCEOF = 46; + /** RegularExpression Id. */ + int MULT = 47; + /** RegularExpression Id. */ + int PLUS = 48; + /** RegularExpression Id. */ + int MINUS = 49; + /** RegularExpression Id. */ + int QUESTIONMARK = 50; + /** RegularExpression Id. */ + int DIV0 = 51; + /** RegularExpression Id. */ + int DIV1 = 52; + /** RegularExpression Id. */ + int MOD0 = 53; + /** RegularExpression Id. */ + int MOD1 = 54; + /** RegularExpression Id. */ + int CONCAT = 55; + /** RegularExpression Id. */ + int ASSIGN = 56; + /** RegularExpression Id. */ + int ARROW = 57; + /** RegularExpression Id. */ + int IDENTIFIER = 58; + /** RegularExpression Id. */ + int IMPL_OBJ_START = 59; + /** RegularExpression Id. */ + int LETTER = 60; + /** RegularExpression Id. */ + int DIGIT = 61; + /** RegularExpression Id. */ + int ILLEGAL_CHARACTER = 62; + + /** Lexical state. */ + int DEFAULT = 0; + /** Lexical state. */ + int IN_EXPRESSION = 1; + /** Lexical state. */ + int IN_MAP = 2; + + /** Literal token values. */ + String[] tokenImage = { "<EOF>", "<LITERAL_EXPRESSION>", "\"${\"", "\"#{\"", "\"\\\\\"", "\" \"", "\"\\t\"", "\"\\n\"", "\"\\r\"", "\"{\"", "\"}\"", + "<INTEGER_LITERAL>", "<FLOATING_POINT_LITERAL>", "<EXPONENT>", "<STRING_LITERAL>", "<BADLY_ESCAPED_STRING_LITERAL>", "\"true\"", "\"false\"", + "\"null\"", "\".\"", "\"(\"", "\")\"", "\"[\"", "\"]\"", "\":\"", "\",\"", "\";\"", "\">\"", "\"gt\"", "\"<\"", "\"lt\"", "\">=\"", "\"ge\"", + "\"<=\"", "\"le\"", "\"==\"", "\"eq\"", "\"!=\"", "\"ne\"", "\"!\"", "\"not\"", "\"&&\"", "\"and\"", "\"||\"", "\"or\"", "\"empty\"", + "\"instanceof\"", "\"*\"", "\"+\"", "\"-\"", "\"?\"", "\"/\"", "\"div\"", "\"%\"", "\"mod\"", "\"+=\"", "\"=\"", "\"->\"", "<IDENTIFIER>", "\"#\"", + "<LETTER>", "<DIGIT>", "<ILLEGAL_CHARACTER>", }; + +}
diff --git a/impl/src/main/java/com/sun/el/parser/ELParserTokenManager.java b/impl/src/main/java/com/sun/el/parser/ELParserTokenManager.java new file mode 100644 index 0000000..027fd5d --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/ELParserTokenManager.java
@@ -0,0 +1,2204 @@ +/* + * Copyright (c) 2018, 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +/* Generated By:JJTree&JavaCC: Do not edit this line. ELParserTokenManager.java */ +package com.sun.el.parser; + +/** Token Manager. */ +public class ELParserTokenManager implements ELParserConstants { + java.util.Stack<Integer> stack = new java.util.Stack<Integer>(); + + /** Debug output. */ + public java.io.PrintStream debugStream = System.out; + + /** Set debug output. */ + public void setDebugStream(java.io.PrintStream ds) { + debugStream = ds; + } + + private final int jjStopStringLiteralDfa_0(int pos, long active0) { + switch (pos) { + case 0: + if ((active0 & 0x10L) != 0L) { + return 2; + } + if ((active0 & 0xcL) != 0L) { + jjmatchedKind = 1; + return 4; + } + return -1; + default: + return -1; + } + } + + private final int jjStartNfa_0(int pos, long active0) { + return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0), pos + 1); + } + + private int jjStopAtPos(int pos, int kind) { + jjmatchedKind = kind; + jjmatchedPos = pos; + return pos + 1; + } + + private int jjMoveStringLiteralDfa0_0() { + switch (curChar) { + case 35: + return jjMoveStringLiteralDfa1_0(0x8L); + case 36: + return jjMoveStringLiteralDfa1_0(0x4L); + case 92: + return jjStartNfaWithStates_0(0, 4, 2); + default: + return jjMoveNfa_0(6, 0); + } + } + + private int jjMoveStringLiteralDfa1_0(long active0) { + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + jjStopStringLiteralDfa_0(0, active0); + return 1; + } + switch (curChar) { + case 123: + if ((active0 & 0x4L) != 0L) { + return jjStopAtPos(1, 2); + } else if ((active0 & 0x8L) != 0L) { + return jjStopAtPos(1, 3); + } + break; + default: + break; + } + return jjStartNfa_0(0, active0); + } + + private int jjStartNfaWithStates_0(int pos, int kind, int state) { + jjmatchedKind = kind; + jjmatchedPos = pos; + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + return pos + 1; + } + return jjMoveNfa_0(state, pos + 1); + } + + static final long[] jjbitVec0 = { 0xfffffffffffffffeL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL }; + static final long[] jjbitVec2 = { 0x0L, 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL }; + + private int jjMoveNfa_0(int startState, int curPos) { + int startsAt = 0; + jjnewStateCnt = 7; + int i = 1; + jjstateSet[0] = startState; + int kind = 0x7fffffff; + for (;;) { + if (++jjround == 0x7fffffff) { + ReInitRounds(); + } + if (curChar < 64) { + long l = 1L << curChar; + do { + switch (jjstateSet[--i]) { + case 6: + if ((0xffffffe7ffffffffL & l) != 0L) { + if (kind > 1) { + kind = 1; + } + jjCheckNAddStates(0, 3); + } else if ((0x1800000000L & l) != 0L) { + if (kind > 1) { + kind = 1; + } + } + if (curChar == 35) { + jjCheckNAdd(4); + } else if (curChar == 36) { + jjCheckNAdd(4); + } + break; + case 0: + case 4: + if ((0xffffffe7ffffffffL & l) == 0L) { + break; + } + if (kind > 1) { + kind = 1; + } + jjCheckNAddStates(0, 3); + break; + case 2: + if ((0x1800000000L & l) == 0L) { + break; + } + if (kind > 1) { + kind = 1; + } + jjCheckNAddStates(0, 3); + break; + case 3: + if (curChar == 36) { + jjCheckNAdd(4); + } + break; + case 5: + if (curChar == 35) { + jjCheckNAdd(4); + } + break; + default: + break; + } + } while (i != startsAt); + } else if (curChar < 128) { + long l = 1L << (curChar & 077); + do { + switch (jjstateSet[--i]) { + case 6: + if ((0xffffffffefffffffL & l) != 0L) { + if (kind > 1) { + kind = 1; + } + jjCheckNAddStates(0, 3); + } else if (curChar == 92) { + jjstateSet[jjnewStateCnt++] = 2; + } + break; + case 0: + if ((0xffffffffefffffffL & l) == 0L) { + break; + } + if (kind > 1) { + kind = 1; + } + jjCheckNAddStates(0, 3); + break; + case 1: + if (curChar == 92) { + jjstateSet[jjnewStateCnt++] = 2; + } + break; + case 2: + if (curChar != 92) { + break; + } + if (kind > 1) { + kind = 1; + } + jjCheckNAddStates(0, 3); + break; + case 4: + if ((0xf7ffffffefffffffL & l) == 0L) { + break; + } + if (kind > 1) { + kind = 1; + } + jjCheckNAddStates(0, 3); + break; + default: + break; + } + } while (i != startsAt); + } else { + int hiByte = curChar >> 8; + int i1 = hiByte >> 6; + long l1 = 1L << (hiByte & 077); + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + do { + switch (jjstateSet[--i]) { + case 6: + case 0: + case 4: + if (!jjCanMove_0(hiByte, i1, i2, l1, l2)) { + break; + } + if (kind > 1) { + kind = 1; + } + jjCheckNAddStates(0, 3); + break; + default: + break; + } + } while (i != startsAt); + } + if (kind != 0x7fffffff) { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + if ((i = jjnewStateCnt) == (startsAt = 7 - (jjnewStateCnt = startsAt))) { + return curPos; + } + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + return curPos; + } + } + } + + private final int jjStopStringLiteralDfa_2(int pos, long active0) { + switch (pos) { + case 0: + if ((active0 & 0x80000L) != 0L) { + return 1; + } + if ((active0 & 0x50755550070000L) != 0L) { + jjmatchedKind = 58; + return 6; + } + return -1; + case 1: + if ((active0 & 0x105550000000L) != 0L) { + return 6; + } + if ((active0 & 0x50650000070000L) != 0L) { + jjmatchedKind = 58; + jjmatchedPos = 1; + return 6; + } + return -1; + case 2: + if ((active0 & 0x50050000000000L) != 0L) { + return 6; + } + if ((active0 & 0x600000070000L) != 0L) { + jjmatchedKind = 58; + jjmatchedPos = 2; + return 6; + } + return -1; + case 3: + if ((active0 & 0x50000L) != 0L) { + return 6; + } + if ((active0 & 0x600000020000L) != 0L) { + jjmatchedKind = 58; + jjmatchedPos = 3; + return 6; + } + return -1; + case 4: + if ((active0 & 0x400000000000L) != 0L) { + jjmatchedKind = 58; + jjmatchedPos = 4; + return 6; + } + if ((active0 & 0x200000020000L) != 0L) { + return 6; + } + return -1; + case 5: + if ((active0 & 0x400000000000L) != 0L) { + jjmatchedKind = 58; + jjmatchedPos = 5; + return 6; + } + return -1; + case 6: + if ((active0 & 0x400000000000L) != 0L) { + jjmatchedKind = 58; + jjmatchedPos = 6; + return 6; + } + return -1; + case 7: + if ((active0 & 0x400000000000L) != 0L) { + jjmatchedKind = 58; + jjmatchedPos = 7; + return 6; + } + return -1; + case 8: + if ((active0 & 0x400000000000L) != 0L) { + jjmatchedKind = 58; + jjmatchedPos = 8; + return 6; + } + return -1; + default: + return -1; + } + } + + private final int jjStartNfa_2(int pos, long active0) { + return jjMoveNfa_2(jjStopStringLiteralDfa_2(pos, active0), pos + 1); + } + + private int jjMoveStringLiteralDfa0_2() { + switch (curChar) { + case 33: + jjmatchedKind = 39; + return jjMoveStringLiteralDfa1_2(0x2000000000L); + case 37: + return jjStopAtPos(0, 53); + case 38: + return jjMoveStringLiteralDfa1_2(0x20000000000L); + case 40: + return jjStopAtPos(0, 20); + case 41: + return jjStopAtPos(0, 21); + case 42: + return jjStopAtPos(0, 47); + case 43: + jjmatchedKind = 48; + return jjMoveStringLiteralDfa1_2(0x80000000000000L); + case 44: + return jjStopAtPos(0, 25); + case 45: + jjmatchedKind = 49; + return jjMoveStringLiteralDfa1_2(0x200000000000000L); + case 46: + return jjStartNfaWithStates_2(0, 19, 1); + case 47: + return jjStopAtPos(0, 51); + case 58: + return jjStopAtPos(0, 24); + case 59: + return jjStopAtPos(0, 26); + case 60: + jjmatchedKind = 29; + return jjMoveStringLiteralDfa1_2(0x200000000L); + case 61: + jjmatchedKind = 56; + return jjMoveStringLiteralDfa1_2(0x800000000L); + case 62: + jjmatchedKind = 27; + return jjMoveStringLiteralDfa1_2(0x80000000L); + case 63: + return jjStopAtPos(0, 50); + case 91: + return jjStopAtPos(0, 22); + case 93: + return jjStopAtPos(0, 23); + case 97: + return jjMoveStringLiteralDfa1_2(0x40000000000L); + case 100: + return jjMoveStringLiteralDfa1_2(0x10000000000000L); + case 101: + return jjMoveStringLiteralDfa1_2(0x201000000000L); + case 102: + return jjMoveStringLiteralDfa1_2(0x20000L); + case 103: + return jjMoveStringLiteralDfa1_2(0x110000000L); + case 105: + return jjMoveStringLiteralDfa1_2(0x400000000000L); + case 108: + return jjMoveStringLiteralDfa1_2(0x440000000L); + case 109: + return jjMoveStringLiteralDfa1_2(0x40000000000000L); + case 110: + return jjMoveStringLiteralDfa1_2(0x14000040000L); + case 111: + return jjMoveStringLiteralDfa1_2(0x100000000000L); + case 116: + return jjMoveStringLiteralDfa1_2(0x10000L); + case 123: + return jjStopAtPos(0, 9); + case 124: + return jjMoveStringLiteralDfa1_2(0x80000000000L); + case 125: + return jjStopAtPos(0, 10); + default: + return jjMoveNfa_2(0, 0); + } + } + + private int jjMoveStringLiteralDfa1_2(long active0) { + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + jjStopStringLiteralDfa_2(0, active0); + return 1; + } + switch (curChar) { + case 38: + if ((active0 & 0x20000000000L) != 0L) { + return jjStopAtPos(1, 41); + } + break; + case 61: + if ((active0 & 0x80000000L) != 0L) { + return jjStopAtPos(1, 31); + } else if ((active0 & 0x200000000L) != 0L) { + return jjStopAtPos(1, 33); + } else if ((active0 & 0x800000000L) != 0L) { + return jjStopAtPos(1, 35); + } else if ((active0 & 0x2000000000L) != 0L) { + return jjStopAtPos(1, 37); + } else if ((active0 & 0x80000000000000L) != 0L) { + return jjStopAtPos(1, 55); + } + break; + case 62: + if ((active0 & 0x200000000000000L) != 0L) { + return jjStopAtPos(1, 57); + } + break; + case 97: + return jjMoveStringLiteralDfa2_2(active0, 0x20000L); + case 101: + if ((active0 & 0x100000000L) != 0L) { + return jjStartNfaWithStates_2(1, 32, 6); + } else if ((active0 & 0x400000000L) != 0L) { + return jjStartNfaWithStates_2(1, 34, 6); + } else if ((active0 & 0x4000000000L) != 0L) { + return jjStartNfaWithStates_2(1, 38, 6); + } + break; + case 105: + return jjMoveStringLiteralDfa2_2(active0, 0x10000000000000L); + case 109: + return jjMoveStringLiteralDfa2_2(active0, 0x200000000000L); + case 110: + return jjMoveStringLiteralDfa2_2(active0, 0x440000000000L); + case 111: + return jjMoveStringLiteralDfa2_2(active0, 0x40010000000000L); + case 113: + if ((active0 & 0x1000000000L) != 0L) { + return jjStartNfaWithStates_2(1, 36, 6); + } + break; + case 114: + if ((active0 & 0x100000000000L) != 0L) { + return jjStartNfaWithStates_2(1, 44, 6); + } + return jjMoveStringLiteralDfa2_2(active0, 0x10000L); + case 116: + if ((active0 & 0x10000000L) != 0L) { + return jjStartNfaWithStates_2(1, 28, 6); + } else if ((active0 & 0x40000000L) != 0L) { + return jjStartNfaWithStates_2(1, 30, 6); + } + break; + case 117: + return jjMoveStringLiteralDfa2_2(active0, 0x40000L); + case 124: + if ((active0 & 0x80000000000L) != 0L) { + return jjStopAtPos(1, 43); + } + break; + default: + break; + } + return jjStartNfa_2(0, active0); + } + + private int jjMoveStringLiteralDfa2_2(long old0, long active0) { + if (((active0 &= old0)) == 0L) { + return jjStartNfa_2(0, old0); + } + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + jjStopStringLiteralDfa_2(1, active0); + return 2; + } + switch (curChar) { + case 100: + if ((active0 & 0x40000000000L) != 0L) { + return jjStartNfaWithStates_2(2, 42, 6); + } else if ((active0 & 0x40000000000000L) != 0L) { + return jjStartNfaWithStates_2(2, 54, 6); + } + break; + case 108: + return jjMoveStringLiteralDfa3_2(active0, 0x60000L); + case 112: + return jjMoveStringLiteralDfa3_2(active0, 0x200000000000L); + case 115: + return jjMoveStringLiteralDfa3_2(active0, 0x400000000000L); + case 116: + if ((active0 & 0x10000000000L) != 0L) { + return jjStartNfaWithStates_2(2, 40, 6); + } + break; + case 117: + return jjMoveStringLiteralDfa3_2(active0, 0x10000L); + case 118: + if ((active0 & 0x10000000000000L) != 0L) { + return jjStartNfaWithStates_2(2, 52, 6); + } + break; + default: + break; + } + return jjStartNfa_2(1, active0); + } + + private int jjMoveStringLiteralDfa3_2(long old0, long active0) { + if (((active0 &= old0)) == 0L) { + return jjStartNfa_2(1, old0); + } + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + jjStopStringLiteralDfa_2(2, active0); + return 3; + } + switch (curChar) { + case 101: + if ((active0 & 0x10000L) != 0L) { + return jjStartNfaWithStates_2(3, 16, 6); + } + break; + case 108: + if ((active0 & 0x40000L) != 0L) { + return jjStartNfaWithStates_2(3, 18, 6); + } + break; + case 115: + return jjMoveStringLiteralDfa4_2(active0, 0x20000L); + case 116: + return jjMoveStringLiteralDfa4_2(active0, 0x600000000000L); + default: + break; + } + return jjStartNfa_2(2, active0); + } + + private int jjMoveStringLiteralDfa4_2(long old0, long active0) { + if (((active0 &= old0)) == 0L) { + return jjStartNfa_2(2, old0); + } + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + jjStopStringLiteralDfa_2(3, active0); + return 4; + } + switch (curChar) { + case 97: + return jjMoveStringLiteralDfa5_2(active0, 0x400000000000L); + case 101: + if ((active0 & 0x20000L) != 0L) { + return jjStartNfaWithStates_2(4, 17, 6); + } + break; + case 121: + if ((active0 & 0x200000000000L) != 0L) { + return jjStartNfaWithStates_2(4, 45, 6); + } + break; + default: + break; + } + return jjStartNfa_2(3, active0); + } + + private int jjMoveStringLiteralDfa5_2(long old0, long active0) { + if (((active0 &= old0)) == 0L) { + return jjStartNfa_2(3, old0); + } + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + jjStopStringLiteralDfa_2(4, active0); + return 5; + } + switch (curChar) { + case 110: + return jjMoveStringLiteralDfa6_2(active0, 0x400000000000L); + default: + break; + } + return jjStartNfa_2(4, active0); + } + + private int jjMoveStringLiteralDfa6_2(long old0, long active0) { + if (((active0 &= old0)) == 0L) { + return jjStartNfa_2(4, old0); + } + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + jjStopStringLiteralDfa_2(5, active0); + return 6; + } + switch (curChar) { + case 99: + return jjMoveStringLiteralDfa7_2(active0, 0x400000000000L); + default: + break; + } + return jjStartNfa_2(5, active0); + } + + private int jjMoveStringLiteralDfa7_2(long old0, long active0) { + if (((active0 &= old0)) == 0L) { + return jjStartNfa_2(5, old0); + } + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + jjStopStringLiteralDfa_2(6, active0); + return 7; + } + switch (curChar) { + case 101: + return jjMoveStringLiteralDfa8_2(active0, 0x400000000000L); + default: + break; + } + return jjStartNfa_2(6, active0); + } + + private int jjMoveStringLiteralDfa8_2(long old0, long active0) { + if (((active0 &= old0)) == 0L) { + return jjStartNfa_2(6, old0); + } + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + jjStopStringLiteralDfa_2(7, active0); + return 8; + } + switch (curChar) { + case 111: + return jjMoveStringLiteralDfa9_2(active0, 0x400000000000L); + default: + break; + } + return jjStartNfa_2(7, active0); + } + + private int jjMoveStringLiteralDfa9_2(long old0, long active0) { + if (((active0 &= old0)) == 0L) { + return jjStartNfa_2(7, old0); + } + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + jjStopStringLiteralDfa_2(8, active0); + return 9; + } + switch (curChar) { + case 102: + if ((active0 & 0x400000000000L) != 0L) { + return jjStartNfaWithStates_2(9, 46, 6); + } + break; + default: + break; + } + return jjStartNfa_2(8, active0); + } + + private int jjStartNfaWithStates_2(int pos, int kind, int state) { + jjmatchedKind = kind; + jjmatchedPos = pos; + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + return pos + 1; + } + return jjMoveNfa_2(state, pos + 1); + } + + static final long[] jjbitVec3 = { 0x1ff00000fffffffeL, 0xffffffffffffc000L, 0xffffffffL, 0x600000000000000L }; + static final long[] jjbitVec4 = { 0x0L, 0x0L, 0x0L, 0xff7fffffff7fffffL }; + static final long[] jjbitVec5 = { 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL }; + static final long[] jjbitVec6 = { 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffL, 0x0L }; + static final long[] jjbitVec7 = { 0xffffffffffffffffL, 0xffffffffffffffffL, 0x0L, 0x0L }; + static final long[] jjbitVec8 = { 0x3fffffffffffL, 0x0L, 0x0L, 0x0L }; + + private int jjMoveNfa_2(int startState, int curPos) { + int startsAt = 0; + jjnewStateCnt = 35; + int i = 1; + jjstateSet[0] = startState; + int kind = 0x7fffffff; + for (;;) { + if (++jjround == 0x7fffffff) { + ReInitRounds(); + } + if (curChar < 64) { + long l = 1L << curChar; + do { + switch (jjstateSet[--i]) { + case 0: + if ((0x3ff000000000000L & l) != 0L) { + if (kind > 11) { + kind = 11; + } + jjCheckNAddStates(4, 8); + } else if ((0x1800000000L & l) != 0L) { + if (kind > 58) { + kind = 58; + } + jjCheckNAdd(6); + } else if (curChar == 39) { + jjCheckNAddStates(9, 13); + } else if (curChar == 34) { + jjCheckNAddStates(14, 18); + } else if (curChar == 46) { + jjCheckNAdd(1); + } + break; + case 1: + if ((0x3ff000000000000L & l) == 0L) { + break; + } + if (kind > 12) { + kind = 12; + } + jjCheckNAddTwoStates(1, 2); + break; + case 3: + if ((0x280000000000L & l) != 0L) { + jjCheckNAdd(4); + } + break; + case 4: + if ((0x3ff000000000000L & l) == 0L) { + break; + } + if (kind > 12) { + kind = 12; + } + jjCheckNAdd(4); + break; + case 5: + if ((0x1800000000L & l) == 0L) { + break; + } + if (kind > 58) { + kind = 58; + } + jjCheckNAdd(6); + break; + case 6: + if ((0x3ff001000000000L & l) == 0L) { + break; + } + if (kind > 58) { + kind = 58; + } + jjCheckNAdd(6); + break; + case 7: + if ((0x3ff000000000000L & l) == 0L) { + break; + } + if (kind > 11) { + kind = 11; + } + jjCheckNAddStates(4, 8); + break; + case 8: + if ((0x3ff000000000000L & l) == 0L) { + break; + } + if (kind > 11) { + kind = 11; + } + jjCheckNAdd(8); + break; + case 9: + if ((0x3ff000000000000L & l) != 0L) { + jjCheckNAddTwoStates(9, 10); + } + break; + case 10: + if (curChar != 46) { + break; + } + if (kind > 12) { + kind = 12; + } + jjCheckNAddTwoStates(11, 12); + break; + case 11: + if ((0x3ff000000000000L & l) == 0L) { + break; + } + if (kind > 12) { + kind = 12; + } + jjCheckNAddTwoStates(11, 12); + break; + case 13: + if ((0x280000000000L & l) != 0L) { + jjCheckNAdd(14); + } + break; + case 14: + if ((0x3ff000000000000L & l) == 0L) { + break; + } + if (kind > 12) { + kind = 12; + } + jjCheckNAdd(14); + break; + case 15: + if ((0x3ff000000000000L & l) != 0L) { + jjCheckNAddTwoStates(15, 16); + } + break; + case 17: + if ((0x280000000000L & l) != 0L) { + jjCheckNAdd(18); + } + break; + case 18: + if ((0x3ff000000000000L & l) == 0L) { + break; + } + if (kind > 12) { + kind = 12; + } + jjCheckNAdd(18); + break; + case 19: + if (curChar == 34) { + jjCheckNAddStates(14, 18); + } + break; + case 20: + if ((0xfffffffbffffffffL & l) != 0L) { + jjCheckNAddStates(19, 21); + } + break; + case 22: + if (curChar == 34) { + jjCheckNAddStates(19, 21); + } + break; + case 23: + if (curChar == 34 && kind > 14) { + kind = 14; + } + break; + case 24: + if ((0xfffffffbffffffffL & l) != 0L) { + jjCheckNAddTwoStates(24, 25); + } + break; + case 26: + if ((0xfffffffbffffffffL & l) != 0L && kind > 15) { + kind = 15; + } + break; + case 27: + if (curChar == 39) { + jjCheckNAddStates(9, 13); + } + break; + case 28: + if ((0xffffff7fffffffffL & l) != 0L) { + jjCheckNAddStates(22, 24); + } + break; + case 30: + if (curChar == 39) { + jjCheckNAddStates(22, 24); + } + break; + case 31: + if (curChar == 39 && kind > 14) { + kind = 14; + } + break; + case 32: + if ((0xffffff7fffffffffL & l) != 0L) { + jjCheckNAddTwoStates(32, 33); + } + break; + case 34: + if ((0xffffff7fffffffffL & l) != 0L && kind > 15) { + kind = 15; + } + break; + default: + break; + } + } while (i != startsAt); + } else if (curChar < 128) { + long l = 1L << (curChar & 077); + do { + switch (jjstateSet[--i]) { + case 0: + case 6: + if ((0x7fffffe87fffffeL & l) == 0L) { + break; + } + if (kind > 58) { + kind = 58; + } + jjCheckNAdd(6); + break; + case 2: + if ((0x2000000020L & l) != 0L) { + jjAddStates(25, 26); + } + break; + case 12: + if ((0x2000000020L & l) != 0L) { + jjAddStates(27, 28); + } + break; + case 16: + if ((0x2000000020L & l) != 0L) { + jjAddStates(29, 30); + } + break; + case 20: + if ((0xffffffffefffffffL & l) != 0L) { + jjCheckNAddStates(19, 21); + } + break; + case 21: + if (curChar == 92) { + jjstateSet[jjnewStateCnt++] = 22; + } + break; + case 22: + if (curChar == 92) { + jjCheckNAddStates(19, 21); + } + break; + case 24: + if ((0xffffffffefffffffL & l) != 0L) { + jjAddStates(31, 32); + } + break; + case 25: + if (curChar == 92) { + jjstateSet[jjnewStateCnt++] = 26; + } + break; + case 26: + case 34: + if ((0xffffffffefffffffL & l) != 0L && kind > 15) { + kind = 15; + } + break; + case 28: + if ((0xffffffffefffffffL & l) != 0L) { + jjCheckNAddStates(22, 24); + } + break; + case 29: + if (curChar == 92) { + jjstateSet[jjnewStateCnt++] = 30; + } + break; + case 30: + if (curChar == 92) { + jjCheckNAddStates(22, 24); + } + break; + case 32: + if ((0xffffffffefffffffL & l) != 0L) { + jjAddStates(33, 34); + } + break; + case 33: + if (curChar == 92) { + jjstateSet[jjnewStateCnt++] = 34; + } + break; + default: + break; + } + } while (i != startsAt); + } else { + int hiByte = curChar >> 8; + int i1 = hiByte >> 6; + long l1 = 1L << (hiByte & 077); + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + do { + switch (jjstateSet[--i]) { + case 0: + case 6: + if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) { + break; + } + if (kind > 58) { + kind = 58; + } + jjCheckNAdd(6); + break; + case 20: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) { + jjAddStates(19, 21); + } + break; + case 24: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) { + jjAddStates(31, 32); + } + break; + case 26: + case 34: + if (jjCanMove_0(hiByte, i1, i2, l1, l2) && kind > 15) { + kind = 15; + } + break; + case 28: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) { + jjAddStates(22, 24); + } + break; + case 32: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) { + jjAddStates(33, 34); + } + break; + default: + break; + } + } while (i != startsAt); + } + if (kind != 0x7fffffff) { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + if ((i = jjnewStateCnt) == (startsAt = 35 - (jjnewStateCnt = startsAt))) { + return curPos; + } + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + return curPos; + } + } + } + + private final int jjStopStringLiteralDfa_1(int pos, long active0) { + switch (pos) { + case 0: + if ((active0 & 0x80000L) != 0L) { + return 1; + } + if ((active0 & 0x50755550070000L) != 0L) { + jjmatchedKind = 58; + return 6; + } + return -1; + case 1: + if ((active0 & 0x105550000000L) != 0L) { + return 6; + } + if ((active0 & 0x50650000070000L) != 0L) { + jjmatchedKind = 58; + jjmatchedPos = 1; + return 6; + } + return -1; + case 2: + if ((active0 & 0x50050000000000L) != 0L) { + return 6; + } + if ((active0 & 0x600000070000L) != 0L) { + jjmatchedKind = 58; + jjmatchedPos = 2; + return 6; + } + return -1; + case 3: + if ((active0 & 0x50000L) != 0L) { + return 6; + } + if ((active0 & 0x600000020000L) != 0L) { + jjmatchedKind = 58; + jjmatchedPos = 3; + return 6; + } + return -1; + case 4: + if ((active0 & 0x400000000000L) != 0L) { + jjmatchedKind = 58; + jjmatchedPos = 4; + return 6; + } + if ((active0 & 0x200000020000L) != 0L) { + return 6; + } + return -1; + case 5: + if ((active0 & 0x400000000000L) != 0L) { + jjmatchedKind = 58; + jjmatchedPos = 5; + return 6; + } + return -1; + case 6: + if ((active0 & 0x400000000000L) != 0L) { + jjmatchedKind = 58; + jjmatchedPos = 6; + return 6; + } + return -1; + case 7: + if ((active0 & 0x400000000000L) != 0L) { + jjmatchedKind = 58; + jjmatchedPos = 7; + return 6; + } + return -1; + case 8: + if ((active0 & 0x400000000000L) != 0L) { + jjmatchedKind = 58; + jjmatchedPos = 8; + return 6; + } + return -1; + default: + return -1; + } + } + + private final int jjStartNfa_1(int pos, long active0) { + return jjMoveNfa_1(jjStopStringLiteralDfa_1(pos, active0), pos + 1); + } + + private int jjMoveStringLiteralDfa0_1() { + switch (curChar) { + case 33: + jjmatchedKind = 39; + return jjMoveStringLiteralDfa1_1(0x2000000000L); + case 37: + return jjStopAtPos(0, 53); + case 38: + return jjMoveStringLiteralDfa1_1(0x20000000000L); + case 40: + return jjStopAtPos(0, 20); + case 41: + return jjStopAtPos(0, 21); + case 42: + return jjStopAtPos(0, 47); + case 43: + jjmatchedKind = 48; + return jjMoveStringLiteralDfa1_1(0x80000000000000L); + case 44: + return jjStopAtPos(0, 25); + case 45: + jjmatchedKind = 49; + return jjMoveStringLiteralDfa1_1(0x200000000000000L); + case 46: + return jjStartNfaWithStates_1(0, 19, 1); + case 47: + return jjStopAtPos(0, 51); + case 58: + return jjStopAtPos(0, 24); + case 59: + return jjStopAtPos(0, 26); + case 60: + jjmatchedKind = 29; + return jjMoveStringLiteralDfa1_1(0x200000000L); + case 61: + jjmatchedKind = 56; + return jjMoveStringLiteralDfa1_1(0x800000000L); + case 62: + jjmatchedKind = 27; + return jjMoveStringLiteralDfa1_1(0x80000000L); + case 63: + return jjStopAtPos(0, 50); + case 91: + return jjStopAtPos(0, 22); + case 93: + return jjStopAtPos(0, 23); + case 97: + return jjMoveStringLiteralDfa1_1(0x40000000000L); + case 100: + return jjMoveStringLiteralDfa1_1(0x10000000000000L); + case 101: + return jjMoveStringLiteralDfa1_1(0x201000000000L); + case 102: + return jjMoveStringLiteralDfa1_1(0x20000L); + case 103: + return jjMoveStringLiteralDfa1_1(0x110000000L); + case 105: + return jjMoveStringLiteralDfa1_1(0x400000000000L); + case 108: + return jjMoveStringLiteralDfa1_1(0x440000000L); + case 109: + return jjMoveStringLiteralDfa1_1(0x40000000000000L); + case 110: + return jjMoveStringLiteralDfa1_1(0x14000040000L); + case 111: + return jjMoveStringLiteralDfa1_1(0x100000000000L); + case 116: + return jjMoveStringLiteralDfa1_1(0x10000L); + case 123: + return jjStopAtPos(0, 9); + case 124: + return jjMoveStringLiteralDfa1_1(0x80000000000L); + case 125: + return jjStopAtPos(0, 10); + default: + return jjMoveNfa_1(0, 0); + } + } + + private int jjMoveStringLiteralDfa1_1(long active0) { + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + jjStopStringLiteralDfa_1(0, active0); + return 1; + } + switch (curChar) { + case 38: + if ((active0 & 0x20000000000L) != 0L) { + return jjStopAtPos(1, 41); + } + break; + case 61: + if ((active0 & 0x80000000L) != 0L) { + return jjStopAtPos(1, 31); + } else if ((active0 & 0x200000000L) != 0L) { + return jjStopAtPos(1, 33); + } else if ((active0 & 0x800000000L) != 0L) { + return jjStopAtPos(1, 35); + } else if ((active0 & 0x2000000000L) != 0L) { + return jjStopAtPos(1, 37); + } else if ((active0 & 0x80000000000000L) != 0L) { + return jjStopAtPos(1, 55); + } + break; + case 62: + if ((active0 & 0x200000000000000L) != 0L) { + return jjStopAtPos(1, 57); + } + break; + case 97: + return jjMoveStringLiteralDfa2_1(active0, 0x20000L); + case 101: + if ((active0 & 0x100000000L) != 0L) { + return jjStartNfaWithStates_1(1, 32, 6); + } else if ((active0 & 0x400000000L) != 0L) { + return jjStartNfaWithStates_1(1, 34, 6); + } else if ((active0 & 0x4000000000L) != 0L) { + return jjStartNfaWithStates_1(1, 38, 6); + } + break; + case 105: + return jjMoveStringLiteralDfa2_1(active0, 0x10000000000000L); + case 109: + return jjMoveStringLiteralDfa2_1(active0, 0x200000000000L); + case 110: + return jjMoveStringLiteralDfa2_1(active0, 0x440000000000L); + case 111: + return jjMoveStringLiteralDfa2_1(active0, 0x40010000000000L); + case 113: + if ((active0 & 0x1000000000L) != 0L) { + return jjStartNfaWithStates_1(1, 36, 6); + } + break; + case 114: + if ((active0 & 0x100000000000L) != 0L) { + return jjStartNfaWithStates_1(1, 44, 6); + } + return jjMoveStringLiteralDfa2_1(active0, 0x10000L); + case 116: + if ((active0 & 0x10000000L) != 0L) { + return jjStartNfaWithStates_1(1, 28, 6); + } else if ((active0 & 0x40000000L) != 0L) { + return jjStartNfaWithStates_1(1, 30, 6); + } + break; + case 117: + return jjMoveStringLiteralDfa2_1(active0, 0x40000L); + case 124: + if ((active0 & 0x80000000000L) != 0L) { + return jjStopAtPos(1, 43); + } + break; + default: + break; + } + return jjStartNfa_1(0, active0); + } + + private int jjMoveStringLiteralDfa2_1(long old0, long active0) { + if (((active0 &= old0)) == 0L) { + return jjStartNfa_1(0, old0); + } + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + jjStopStringLiteralDfa_1(1, active0); + return 2; + } + switch (curChar) { + case 100: + if ((active0 & 0x40000000000L) != 0L) { + return jjStartNfaWithStates_1(2, 42, 6); + } else if ((active0 & 0x40000000000000L) != 0L) { + return jjStartNfaWithStates_1(2, 54, 6); + } + break; + case 108: + return jjMoveStringLiteralDfa3_1(active0, 0x60000L); + case 112: + return jjMoveStringLiteralDfa3_1(active0, 0x200000000000L); + case 115: + return jjMoveStringLiteralDfa3_1(active0, 0x400000000000L); + case 116: + if ((active0 & 0x10000000000L) != 0L) { + return jjStartNfaWithStates_1(2, 40, 6); + } + break; + case 117: + return jjMoveStringLiteralDfa3_1(active0, 0x10000L); + case 118: + if ((active0 & 0x10000000000000L) != 0L) { + return jjStartNfaWithStates_1(2, 52, 6); + } + break; + default: + break; + } + return jjStartNfa_1(1, active0); + } + + private int jjMoveStringLiteralDfa3_1(long old0, long active0) { + if (((active0 &= old0)) == 0L) { + return jjStartNfa_1(1, old0); + } + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + jjStopStringLiteralDfa_1(2, active0); + return 3; + } + switch (curChar) { + case 101: + if ((active0 & 0x10000L) != 0L) { + return jjStartNfaWithStates_1(3, 16, 6); + } + break; + case 108: + if ((active0 & 0x40000L) != 0L) { + return jjStartNfaWithStates_1(3, 18, 6); + } + break; + case 115: + return jjMoveStringLiteralDfa4_1(active0, 0x20000L); + case 116: + return jjMoveStringLiteralDfa4_1(active0, 0x600000000000L); + default: + break; + } + return jjStartNfa_1(2, active0); + } + + private int jjMoveStringLiteralDfa4_1(long old0, long active0) { + if (((active0 &= old0)) == 0L) { + return jjStartNfa_1(2, old0); + } + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + jjStopStringLiteralDfa_1(3, active0); + return 4; + } + switch (curChar) { + case 97: + return jjMoveStringLiteralDfa5_1(active0, 0x400000000000L); + case 101: + if ((active0 & 0x20000L) != 0L) { + return jjStartNfaWithStates_1(4, 17, 6); + } + break; + case 121: + if ((active0 & 0x200000000000L) != 0L) { + return jjStartNfaWithStates_1(4, 45, 6); + } + break; + default: + break; + } + return jjStartNfa_1(3, active0); + } + + private int jjMoveStringLiteralDfa5_1(long old0, long active0) { + if (((active0 &= old0)) == 0L) { + return jjStartNfa_1(3, old0); + } + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + jjStopStringLiteralDfa_1(4, active0); + return 5; + } + switch (curChar) { + case 110: + return jjMoveStringLiteralDfa6_1(active0, 0x400000000000L); + default: + break; + } + return jjStartNfa_1(4, active0); + } + + private int jjMoveStringLiteralDfa6_1(long old0, long active0) { + if (((active0 &= old0)) == 0L) { + return jjStartNfa_1(4, old0); + } + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + jjStopStringLiteralDfa_1(5, active0); + return 6; + } + switch (curChar) { + case 99: + return jjMoveStringLiteralDfa7_1(active0, 0x400000000000L); + default: + break; + } + return jjStartNfa_1(5, active0); + } + + private int jjMoveStringLiteralDfa7_1(long old0, long active0) { + if (((active0 &= old0)) == 0L) { + return jjStartNfa_1(5, old0); + } + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + jjStopStringLiteralDfa_1(6, active0); + return 7; + } + switch (curChar) { + case 101: + return jjMoveStringLiteralDfa8_1(active0, 0x400000000000L); + default: + break; + } + return jjStartNfa_1(6, active0); + } + + private int jjMoveStringLiteralDfa8_1(long old0, long active0) { + if (((active0 &= old0)) == 0L) { + return jjStartNfa_1(6, old0); + } + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + jjStopStringLiteralDfa_1(7, active0); + return 8; + } + switch (curChar) { + case 111: + return jjMoveStringLiteralDfa9_1(active0, 0x400000000000L); + default: + break; + } + return jjStartNfa_1(7, active0); + } + + private int jjMoveStringLiteralDfa9_1(long old0, long active0) { + if (((active0 &= old0)) == 0L) { + return jjStartNfa_1(7, old0); + } + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + jjStopStringLiteralDfa_1(8, active0); + return 9; + } + switch (curChar) { + case 102: + if ((active0 & 0x400000000000L) != 0L) { + return jjStartNfaWithStates_1(9, 46, 6); + } + break; + default: + break; + } + return jjStartNfa_1(8, active0); + } + + private int jjStartNfaWithStates_1(int pos, int kind, int state) { + jjmatchedKind = kind; + jjmatchedPos = pos; + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + return pos + 1; + } + return jjMoveNfa_1(state, pos + 1); + } + + private int jjMoveNfa_1(int startState, int curPos) { + int startsAt = 0; + jjnewStateCnt = 35; + int i = 1; + jjstateSet[0] = startState; + int kind = 0x7fffffff; + for (;;) { + if (++jjround == 0x7fffffff) { + ReInitRounds(); + } + if (curChar < 64) { + long l = 1L << curChar; + do { + switch (jjstateSet[--i]) { + case 0: + if ((0x3ff000000000000L & l) != 0L) { + if (kind > 11) { + kind = 11; + } + jjCheckNAddStates(4, 8); + } else if ((0x1800000000L & l) != 0L) { + if (kind > 58) { + kind = 58; + } + jjCheckNAdd(6); + } else if (curChar == 39) { + jjCheckNAddStates(9, 13); + } else if (curChar == 34) { + jjCheckNAddStates(14, 18); + } else if (curChar == 46) { + jjCheckNAdd(1); + } + break; + case 1: + if ((0x3ff000000000000L & l) == 0L) { + break; + } + if (kind > 12) { + kind = 12; + } + jjCheckNAddTwoStates(1, 2); + break; + case 3: + if ((0x280000000000L & l) != 0L) { + jjCheckNAdd(4); + } + break; + case 4: + if ((0x3ff000000000000L & l) == 0L) { + break; + } + if (kind > 12) { + kind = 12; + } + jjCheckNAdd(4); + break; + case 5: + if ((0x1800000000L & l) == 0L) { + break; + } + if (kind > 58) { + kind = 58; + } + jjCheckNAdd(6); + break; + case 6: + if ((0x3ff001000000000L & l) == 0L) { + break; + } + if (kind > 58) { + kind = 58; + } + jjCheckNAdd(6); + break; + case 7: + if ((0x3ff000000000000L & l) == 0L) { + break; + } + if (kind > 11) { + kind = 11; + } + jjCheckNAddStates(4, 8); + break; + case 8: + if ((0x3ff000000000000L & l) == 0L) { + break; + } + if (kind > 11) { + kind = 11; + } + jjCheckNAdd(8); + break; + case 9: + if ((0x3ff000000000000L & l) != 0L) { + jjCheckNAddTwoStates(9, 10); + } + break; + case 10: + if (curChar != 46) { + break; + } + if (kind > 12) { + kind = 12; + } + jjCheckNAddTwoStates(11, 12); + break; + case 11: + if ((0x3ff000000000000L & l) == 0L) { + break; + } + if (kind > 12) { + kind = 12; + } + jjCheckNAddTwoStates(11, 12); + break; + case 13: + if ((0x280000000000L & l) != 0L) { + jjCheckNAdd(14); + } + break; + case 14: + if ((0x3ff000000000000L & l) == 0L) { + break; + } + if (kind > 12) { + kind = 12; + } + jjCheckNAdd(14); + break; + case 15: + if ((0x3ff000000000000L & l) != 0L) { + jjCheckNAddTwoStates(15, 16); + } + break; + case 17: + if ((0x280000000000L & l) != 0L) { + jjCheckNAdd(18); + } + break; + case 18: + if ((0x3ff000000000000L & l) == 0L) { + break; + } + if (kind > 12) { + kind = 12; + } + jjCheckNAdd(18); + break; + case 19: + if (curChar == 34) { + jjCheckNAddStates(14, 18); + } + break; + case 20: + if ((0xfffffffbffffffffL & l) != 0L) { + jjCheckNAddStates(19, 21); + } + break; + case 22: + if (curChar == 34) { + jjCheckNAddStates(19, 21); + } + break; + case 23: + if (curChar == 34 && kind > 14) { + kind = 14; + } + break; + case 24: + if ((0xfffffffbffffffffL & l) != 0L) { + jjCheckNAddTwoStates(24, 25); + } + break; + case 26: + if ((0xfffffffbffffffffL & l) != 0L && kind > 15) { + kind = 15; + } + break; + case 27: + if (curChar == 39) { + jjCheckNAddStates(9, 13); + } + break; + case 28: + if ((0xffffff7fffffffffL & l) != 0L) { + jjCheckNAddStates(22, 24); + } + break; + case 30: + if (curChar == 39) { + jjCheckNAddStates(22, 24); + } + break; + case 31: + if (curChar == 39 && kind > 14) { + kind = 14; + } + break; + case 32: + if ((0xffffff7fffffffffL & l) != 0L) { + jjCheckNAddTwoStates(32, 33); + } + break; + case 34: + if ((0xffffff7fffffffffL & l) != 0L && kind > 15) { + kind = 15; + } + break; + default: + break; + } + } while (i != startsAt); + } else if (curChar < 128) { + long l = 1L << (curChar & 077); + do { + switch (jjstateSet[--i]) { + case 0: + case 6: + if ((0x7fffffe87fffffeL & l) == 0L) { + break; + } + if (kind > 58) { + kind = 58; + } + jjCheckNAdd(6); + break; + case 2: + if ((0x2000000020L & l) != 0L) { + jjAddStates(25, 26); + } + break; + case 12: + if ((0x2000000020L & l) != 0L) { + jjAddStates(27, 28); + } + break; + case 16: + if ((0x2000000020L & l) != 0L) { + jjAddStates(29, 30); + } + break; + case 20: + if ((0xffffffffefffffffL & l) != 0L) { + jjCheckNAddStates(19, 21); + } + break; + case 21: + if (curChar == 92) { + jjstateSet[jjnewStateCnt++] = 22; + } + break; + case 22: + if (curChar == 92) { + jjCheckNAddStates(19, 21); + } + break; + case 24: + if ((0xffffffffefffffffL & l) != 0L) { + jjAddStates(31, 32); + } + break; + case 25: + if (curChar == 92) { + jjstateSet[jjnewStateCnt++] = 26; + } + break; + case 26: + case 34: + if ((0xffffffffefffffffL & l) != 0L && kind > 15) { + kind = 15; + } + break; + case 28: + if ((0xffffffffefffffffL & l) != 0L) { + jjCheckNAddStates(22, 24); + } + break; + case 29: + if (curChar == 92) { + jjstateSet[jjnewStateCnt++] = 30; + } + break; + case 30: + if (curChar == 92) { + jjCheckNAddStates(22, 24); + } + break; + case 32: + if ((0xffffffffefffffffL & l) != 0L) { + jjAddStates(33, 34); + } + break; + case 33: + if (curChar == 92) { + jjstateSet[jjnewStateCnt++] = 34; + } + break; + default: + break; + } + } while (i != startsAt); + } else { + int hiByte = curChar >> 8; + int i1 = hiByte >> 6; + long l1 = 1L << (hiByte & 077); + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + do { + switch (jjstateSet[--i]) { + case 0: + case 6: + if (!jjCanMove_1(hiByte, i1, i2, l1, l2)) { + break; + } + if (kind > 58) { + kind = 58; + } + jjCheckNAdd(6); + break; + case 20: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) { + jjAddStates(19, 21); + } + break; + case 24: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) { + jjAddStates(31, 32); + } + break; + case 26: + case 34: + if (jjCanMove_0(hiByte, i1, i2, l1, l2) && kind > 15) { + kind = 15; + } + break; + case 28: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) { + jjAddStates(22, 24); + } + break; + case 32: + if (jjCanMove_0(hiByte, i1, i2, l1, l2)) { + jjAddStates(33, 34); + } + break; + default: + break; + } + } while (i != startsAt); + } + if (kind != 0x7fffffff) { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + if ((i = jjnewStateCnt) == (startsAt = 35 - (jjnewStateCnt = startsAt))) { + return curPos; + } + try { + curChar = input_stream.readChar(); + } catch (java.io.IOException e) { + return curPos; + } + } + } + + static final int[] jjnextStates = { 0, 1, 3, 5, 8, 9, 10, 15, 16, 28, 29, 31, 32, 33, 20, 21, 23, 24, 25, 20, 21, 23, 28, 29, 31, 3, 4, 13, 14, 17, 18, 24, + 25, 32, 33, }; + + private static final boolean jjCanMove_0(int hiByte, int i1, int i2, long l1, long l2) { + switch (hiByte) { + case 0: + return ((jjbitVec2[i2] & l2) != 0L); + default: + if ((jjbitVec0[i1] & l1) != 0L) { + return true; + } + return false; + } + } + + private static final boolean jjCanMove_1(int hiByte, int i1, int i2, long l1, long l2) { + switch (hiByte) { + case 0: + return ((jjbitVec4[i2] & l2) != 0L); + case 48: + return ((jjbitVec5[i2] & l2) != 0L); + case 49: + return ((jjbitVec6[i2] & l2) != 0L); + case 51: + return ((jjbitVec7[i2] & l2) != 0L); + case 61: + return ((jjbitVec8[i2] & l2) != 0L); + default: + if ((jjbitVec3[i1] & l1) != 0L) { + return true; + } + return false; + } + } + + /** Token literal values. */ + public static final String[] jjstrLiteralImages = { "", null, "\44\173", "\43\173", null, null, null, null, null, "\173", "\175", null, null, null, null, + null, "\164\162\165\145", "\146\141\154\163\145", "\156\165\154\154", "\56", "\50", "\51", "\133", "\135", "\72", "\54", "\73", "\76", "\147\164", + "\74", "\154\164", "\76\75", "\147\145", "\74\75", "\154\145", "\75\75", "\145\161", "\41\75", "\156\145", "\41", "\156\157\164", "\46\46", + "\141\156\144", "\174\174", "\157\162", "\145\155\160\164\171", "\151\156\163\164\141\156\143\145\157\146", "\52", "\53", "\55", "\77", "\57", + "\144\151\166", "\45", "\155\157\144", "\53\75", "\75", "\55\76", null, null, null, null, null, }; + + /** Lexer state names. */ + public static final String[] lexStateNames = { "DEFAULT", "IN_EXPRESSION", "IN_MAP", }; + + /** Lex State array. */ + public static final int[] jjnewLexState = { -1, -1, 1, 1, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, }; + static final long[] jjtoToken = { 0x47ffffffffffde0fL, }; + static final long[] jjtoSkip = { 0x1f0L, }; + protected SimpleCharStream input_stream; + private final int[] jjrounds = new int[35]; + private final int[] jjstateSet = new int[70]; + private final StringBuilder jjimage = new StringBuilder(); + private StringBuilder image = jjimage; + private int jjimageLen; + private int lengthOfMatch; + protected char curChar; + + /** Constructor. */ + public ELParserTokenManager(SimpleCharStream stream) { + if (SimpleCharStream.staticFlag) { + throw new Error("ERROR: Cannot use a static CharStream class with a non-static lexical analyzer."); + } + input_stream = stream; + } + + /** Constructor. */ + public ELParserTokenManager(SimpleCharStream stream, int lexState) { + this(stream); + SwitchTo(lexState); + } + + /** Reinitialise parser. */ + public void ReInit(SimpleCharStream stream) { + jjmatchedPos = jjnewStateCnt = 0; + curLexState = defaultLexState; + input_stream = stream; + ReInitRounds(); + } + + private void ReInitRounds() { + int i; + jjround = 0x80000001; + for (i = 35; i-- > 0;) { + jjrounds[i] = 0x80000000; + } + } + + /** Reinitialise parser. */ + public void ReInit(SimpleCharStream stream, int lexState) { + ReInit(stream); + SwitchTo(lexState); + } + + /** Switch to specified lex state. */ + public void SwitchTo(int lexState) { + if (lexState >= 3 || lexState < 0) { + throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", TokenMgrError.INVALID_LEXICAL_STATE); + } else { + curLexState = lexState; + } + } + + protected Token jjFillToken() { + final Token t; + final String curTokenImage; + final int beginLine; + final int endLine; + final int beginColumn; + final int endColumn; + String im = jjstrLiteralImages[jjmatchedKind]; + curTokenImage = (im == null) ? input_stream.GetImage() : im; + beginLine = input_stream.getBeginLine(); + beginColumn = input_stream.getBeginColumn(); + endLine = input_stream.getEndLine(); + endColumn = input_stream.getEndColumn(); + t = Token.newToken(jjmatchedKind); + t.kind = jjmatchedKind; + t.image = curTokenImage; + + t.beginLine = beginLine; + t.endLine = endLine; + t.beginColumn = beginColumn; + t.endColumn = endColumn; + + return t; + } + + int curLexState = 0; + int defaultLexState = 0; + int jjnewStateCnt; + int jjround; + int jjmatchedPos; + int jjmatchedKind; + + /** Get the next Token. */ + public Token getNextToken() { + Token matchedToken; + int curPos = 0; + + EOFLoop: for (;;) { + try { + curChar = input_stream.BeginToken(); + } catch (java.io.IOException e) { + jjmatchedKind = 0; + matchedToken = jjFillToken(); + return matchedToken; + } + image = jjimage; + image.setLength(0); + jjimageLen = 0; + + switch (curLexState) { + case 0: + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_0(); + break; + case 1: + try { + input_stream.backup(0); + while (curChar <= 32 && (0x100002600L & (1L << curChar)) != 0L) { + curChar = input_stream.BeginToken(); + } + } catch (java.io.IOException e1) { + continue EOFLoop; + } + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_1(); + if (jjmatchedPos == 0 && jjmatchedKind > 62) { + jjmatchedKind = 62; + } + break; + case 2: + try { + input_stream.backup(0); + while (curChar <= 32 && (0x100002600L & (1L << curChar)) != 0L) { + curChar = input_stream.BeginToken(); + } + } catch (java.io.IOException e1) { + continue EOFLoop; + } + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_2(); + if (jjmatchedPos == 0 && jjmatchedKind > 62) { + jjmatchedKind = 62; + } + break; + } + if (jjmatchedKind != 0x7fffffff) { + if (jjmatchedPos + 1 < curPos) { + input_stream.backup(curPos - jjmatchedPos - 1); + } + if ((jjtoToken[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) { + matchedToken = jjFillToken(); + TokenLexicalActions(matchedToken); + if (jjnewLexState[jjmatchedKind] != -1) { + curLexState = jjnewLexState[jjmatchedKind]; + } + return matchedToken; + } else { + if (jjnewLexState[jjmatchedKind] != -1) { + curLexState = jjnewLexState[jjmatchedKind]; + } + continue EOFLoop; + } + } + int error_line = input_stream.getEndLine(); + int error_column = input_stream.getEndColumn(); + String error_after = null; + boolean EOFSeen = false; + try { + input_stream.readChar(); + input_stream.backup(1); + } catch (java.io.IOException e1) { + EOFSeen = true; + error_after = curPos <= 1 ? "" : input_stream.GetImage(); + if (curChar == '\n' || curChar == '\r') { + error_line++; + error_column = 0; + } else { + error_column++; + } + } + if (!EOFSeen) { + input_stream.backup(1); + error_after = curPos <= 1 ? "" : input_stream.GetImage(); + } + throw new TokenMgrError(EOFSeen, curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LEXICAL_ERROR); + } + } + + void TokenLexicalActions(Token matchedToken) { + switch (jjmatchedKind) { + case 2: + image.append(jjstrLiteralImages[2]); + lengthOfMatch = jjstrLiteralImages[2].length(); + stack.push(DEFAULT); + break; + case 3: + image.append(jjstrLiteralImages[3]); + lengthOfMatch = jjstrLiteralImages[3].length(); + stack.push(DEFAULT); + break; + case 9: + image.append(jjstrLiteralImages[9]); + lengthOfMatch = jjstrLiteralImages[9].length(); + stack.push(curLexState); + break; + case 10: + image.append(jjstrLiteralImages[10]); + lengthOfMatch = jjstrLiteralImages[10].length(); + SwitchTo(stack.pop()); + break; + default: + break; + } + } + + private void jjCheckNAdd(int state) { + if (jjrounds[state] != jjround) { + jjstateSet[jjnewStateCnt++] = state; + jjrounds[state] = jjround; + } + } + + private void jjAddStates(int start, int end) { + do { + jjstateSet[jjnewStateCnt++] = jjnextStates[start]; + } while (start++ != end); + } + + private void jjCheckNAddTwoStates(int state1, int state2) { + jjCheckNAdd(state1); + jjCheckNAdd(state2); + } + + private void jjCheckNAddStates(int start, int end) { + do { + jjCheckNAdd(jjnextStates[start]); + } while (start++ != end); + } + +}
diff --git a/impl/src/main/java/com/sun/el/parser/ELParserTreeConstants.java b/impl/src/main/java/com/sun/el/parser/ELParserTreeConstants.java new file mode 100644 index 0000000..8a2762f --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/ELParserTreeConstants.java
@@ -0,0 +1,69 @@ +/* + * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +/* Generated By:JavaCC: Do not edit this line. ELParserTreeConstants.java Version 5.0 */ +package com.sun.el.parser; + +public interface ELParserTreeConstants { + int JJTCOMPOSITEEXPRESSION = 0; + int JJTLITERALEXPRESSION = 1; + int JJTDEFERREDEXPRESSION = 2; + int JJTDYNAMICEXPRESSION = 3; + int JJTVOID = 4; + int JJTSEMICOLON = 5; + int JJTASSIGN = 6; + int JJTLAMBDAEXPRESSION = 7; + int JJTLAMBDAPARAMETERS = 8; + int JJTCHOICE = 9; + int JJTOR = 10; + int JJTAND = 11; + int JJTEQUAL = 12; + int JJTNOTEQUAL = 13; + int JJTLESSTHAN = 14; + int JJTGREATERTHAN = 15; + int JJTLESSTHANEQUAL = 16; + int JJTGREATERTHANEQUAL = 17; + int JJTCONCAT = 18; + int JJTPLUS = 19; + int JJTMINUS = 20; + int JJTMULT = 21; + int JJTDIV = 22; + int JJTMOD = 23; + int JJTNEGATIVE = 24; + int JJTNOT = 25; + int JJTEMPTY = 26; + int JJTVALUE = 27; + int JJTDOTSUFFIX = 28; + int JJTBRACKETSUFFIX = 29; + int JJTMETHODARGUMENTS = 30; + int JJTMAPDATA = 31; + int JJTMAPENTRY = 32; + int JJTLISTDATA = 33; + int JJTIDENTIFIER = 34; + int JJTFUNCTION = 35; + int JJTTRUE = 36; + int JJTFALSE = 37; + int JJTFLOATINGPOINT = 38; + int JJTINTEGER = 39; + int JJTSTRING = 40; + int JJTNULL = 41; + + String[] jjtNodeName = { "CompositeExpression", "LiteralExpression", "DeferredExpression", "DynamicExpression", "void", "SemiColon", "Assign", + "LambdaExpression", "LambdaParameters", "Choice", "Or", "And", "Equal", "NotEqual", "LessThan", "GreaterThan", "LessThanEqual", "GreaterThanEqual", + "Concat", "Plus", "Minus", "Mult", "Div", "Mod", "Negative", "Not", "Empty", "Value", "DotSuffix", "BracketSuffix", "MethodArguments", "MapData", + "MapEntry", "ListData", "Identifier", "Function", "True", "False", "FloatingPoint", "Integer", "String", "Null", }; +} +/* JavaCC - OriginalChecksum=295bae338407e43a1d349f1ce802614a (do not edit this line) */
diff --git a/impl/src/main/java/com/sun/el/parser/JJTELParserState.java b/impl/src/main/java/com/sun/el/parser/JJTELParserState.java new file mode 100644 index 0000000..80c0298 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/JJTELParserState.java
@@ -0,0 +1,140 @@ +/* + * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +/* Generated By:JavaCC: Do not edit this line. JJTELParserState.java Version 5.0 */ +package com.sun.el.parser; + +public class JJTELParserState { + private java.util.List<Node> nodes; + private java.util.List<Integer> marks; + + private int sp; // number of nodes on stack + private int mk; // current mark + private boolean node_created; + + public JJTELParserState() { + nodes = new java.util.ArrayList<Node>(); + marks = new java.util.ArrayList<Integer>(); + sp = 0; + mk = 0; + } + + /* + * Determines whether the current node was actually closed and pushed. This should only be called in the final user + * action of a node scope. + */ + public boolean nodeCreated() { + return node_created; + } + + /* + * Call this to reinitialize the node stack. It is called automatically by the parser's ReInit() method. + */ + public void reset() { + nodes.clear(); + marks.clear(); + sp = 0; + mk = 0; + } + + /* + * Returns the root node of the AST. It only makes sense to call this after a successful parse. + */ + public Node rootNode() { + return nodes.get(0); + } + + /* Pushes a node on to the stack. */ + public void pushNode(Node n) { + nodes.add(n); + ++sp; + } + + /* + * Returns the node on the top of the stack, and remove it from the stack. + */ + public Node popNode() { + if (--sp < mk) { + mk = marks.remove(marks.size() - 1); + } + return nodes.remove(nodes.size() - 1); + } + + /* Returns the node currently on the top of the stack. */ + public Node peekNode() { + return nodes.get(nodes.size() - 1); + } + + /* + * Returns the number of children on the stack in the current node scope. + */ + public int nodeArity() { + return sp - mk; + } + + public void clearNodeScope(Node n) { + while (sp > mk) { + popNode(); + } + mk = marks.remove(marks.size() - 1); + } + + public void openNodeScope(Node n) { + marks.add(mk); + mk = sp; + n.jjtOpen(); + } + + /* + * A definite node is constructed from a specified number of children. That number of nodes are popped from the stack + * and made the children of the definite node. Then the definite node is pushed on to the stack. + */ + public void closeNodeScope(Node n, int num) { + mk = marks.remove(marks.size() - 1); + while (num-- > 0) { + Node c = popNode(); + c.jjtSetParent(n); + n.jjtAddChild(c, num); + } + n.jjtClose(); + pushNode(n); + node_created = true; + } + + /* + * A conditional node is constructed if its condition is true. All the nodes that have been pushed since the node was + * opened are made children of the conditional node, which is then pushed on to the stack. If the condition is false the + * node is not constructed and they are left on the stack. + */ + public void closeNodeScope(Node n, boolean condition) { + if (condition) { + int a = nodeArity(); + mk = marks.remove(marks.size() - 1); + while (a-- > 0) { + Node c = popNode(); + c.jjtSetParent(n); + n.jjtAddChild(c, a); + } + n.jjtClose(); + pushNode(n); + node_created = true; + } else { + mk = marks.remove(marks.size() - 1); + node_created = false; + } + } +} +/* JavaCC - OriginalChecksum=a169ec9bf66edaa6db0c5550b112beee (do not edit this line) */
diff --git a/impl/src/main/java/com/sun/el/parser/Node.java b/impl/src/main/java/com/sun/el/parser/Node.java new file mode 100644 index 0000000..be71187 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/Node.java
@@ -0,0 +1,91 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; +import javax.el.MethodInfo; +import javax.el.ValueReference; + +import com.sun.el.lang.EvaluationContext; + +/* All AST nodes must implement this interface. It provides basic + machinery for constructing the parent and child relationships + between nodes. */ + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public interface Node { + + /** + * This method is called after the node has been made the current node. It indicates that child nodes can now be added + * to it. + */ + void jjtOpen(); + + /** + * This method is called after all the child nodes have been added. + */ + void jjtClose(); + + /** + * This pair of methods are used to inform the node of its parent. + */ + void jjtSetParent(Node n); + + Node jjtGetParent(); + + /** + * This method tells the node to add its argument to the node's list of children. + */ + void jjtAddChild(Node n, int i); + + /** + * This method returns a child node. The children are numbered from zero, left to right. + */ + Node jjtGetChild(int i); + + /** Return the number of children the node has. */ + int jjtGetNumChildren(); + + String getImage(); + + Object getValue(EvaluationContext ctx) throws ELException; + + void setValue(EvaluationContext ctx, Object value) throws ELException; + + Class getType(EvaluationContext ctx) throws ELException; + + ValueReference getValueReference(EvaluationContext ctx) throws ELException; + + boolean isReadOnly(EvaluationContext ctx) throws ELException; + + void accept(NodeVisitor visitor) throws ELException; + + MethodInfo getMethodInfo(EvaluationContext ctx, Class[] paramTypes) throws ELException; + + Object invoke(EvaluationContext ctx, Class[] paramTypes, Object[] paramValues) throws ELException; + + @Override + boolean equals(Object n); + + @Override + int hashCode(); + + boolean isParametersProvided(); +}
diff --git a/impl/src/main/java/com/sun/el/parser/NodeVisitor.java b/impl/src/main/java/com/sun/el/parser/NodeVisitor.java new file mode 100644 index 0000000..e18922c --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/NodeVisitor.java
@@ -0,0 +1,27 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public interface NodeVisitor { + void visit(Node node) throws ELException; +}
diff --git a/impl/src/main/java/com/sun/el/parser/ParseException.java b/impl/src/main/java/com/sun/el/parser/ParseException.java new file mode 100644 index 0000000..ac9dee3 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/ParseException.java
@@ -0,0 +1,185 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +/** + * This exception is thrown when parse errors are encountered. You can explicitly create objects of this exception type + * by calling the method generateParseException in the generated parser. + * + * You can modify this class to customize your error reporting mechanisms so long as you retain the public fields. + */ +public class ParseException extends Exception { + + /** + * This constructor is used by the method "generateParseException" in the generated parser. Calling this constructor + * generates a new object of this type with the fields "currentToken", "expectedTokenSequences", and "tokenImage" set. + * The boolean flag "specialConstructor" is also set to true to indicate that this constructor was used to create this + * object. This constructor calls its super class with the empty string to force the "toString" method of parent class + * "Throwable" to print the error message in the form: ParseException: <result of getMessage> + */ + public ParseException(Token currentTokenVal, int[][] expectedTokenSequencesVal, String[] tokenImageVal) { + super(""); + specialConstructor = true; + currentToken = currentTokenVal; + expectedTokenSequences = expectedTokenSequencesVal; + tokenImage = tokenImageVal; + } + + /** + * The following constructors are for use by you for whatever purpose you can think of. Constructing the exception in + * this manner makes the exception behave in the normal way - i.e., as documented in the class "Throwable". The fields + * "errorToken", "expectedTokenSequences", and "tokenImage" do not contain relevant information. The JavaCC generated + * code does not use these constructors. + */ + + public ParseException() { + super(); + specialConstructor = false; + } + + public ParseException(String message) { + super(message); + specialConstructor = false; + } + + /** + * This variable determines which constructor was used to create this object and thereby affects the semantics of the + * "getMessage" method (see below). + */ + protected boolean specialConstructor; + + /** + * This is the last token that has been consumed successfully. If this object has been created due to a parse error, the + * token followng this token will (therefore) be the first error token. + */ + public Token currentToken; + + /** + * Each entry in this array is an array of integers. Each array of integers represents a sequence of tokens (by their + * ordinal values) that is expected at this point of the parse. + */ + public int[][] expectedTokenSequences; + + /** + * This is a reference to the "tokenImage" array of the generated parser within which the parse error occurred. This + * array is defined in the generated ...Constants interface. + */ + public String[] tokenImage; + + /** + * This method has the standard behavior when this object has been created using the standard constructors. Otherwise, + * it uses "currentToken" and "expectedTokenSequences" to generate a parse error message and returns it. If this object + * has been created due to a parse error, and you do not catch it (it gets thrown from the parser), then this method is + * called during the printing of the final stack trace, and hence the correct error message gets displayed. + */ + @Override + public String getMessage() { + if (!specialConstructor) { + return super.getMessage(); + } + String expected = ""; + int maxSize = 0; + for (int i = 0; i < expectedTokenSequences.length; i++) { + if (maxSize < expectedTokenSequences[i].length) { + maxSize = expectedTokenSequences[i].length; + } + for (int j = 0; j < expectedTokenSequences[i].length; j++) { + expected += tokenImage[expectedTokenSequences[i][j]] + " "; + } + if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) { + expected += "..."; + } + expected += eol + " "; + } + String retval = "Encountered \""; + Token tok = currentToken.next; + for (int i = 0; i < maxSize; i++) { + if (i != 0) { + retval += " "; + } + if (tok.kind == 0) { + retval += tokenImage[0]; + break; + } + retval += add_escapes(tok.image); + tok = tok.next; + } + retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn; + retval += "." + eol; + if (expectedTokenSequences.length == 1) { + retval += "Was expecting:" + eol + " "; + } else { + retval += "Was expecting one of:" + eol + " "; + } + retval += expected; + return retval; + } + + /** + * The end of line string for this machine. + */ + protected String eol = System.getProperty("line.separator", "\n"); + + /** + * Used to convert raw characters to their escaped version when these raw version cannot be used as part of an ASCII + * string literal. + */ + protected String add_escapes(String str) { + StringBuffer retval = new StringBuffer(); + char ch; + for (int i = 0; i < str.length(); i++) { + switch (str.charAt(i)) { + case 0: + continue; + case '\b': + retval.append("\\b"); + continue; + case '\t': + retval.append("\\t"); + continue; + case '\n': + retval.append("\\n"); + continue; + case '\f': + retval.append("\\f"); + continue; + case '\r': + retval.append("\\r"); + continue; + case '\"': + retval.append("\\\""); + continue; + case '\'': + retval.append("\\\'"); + continue; + case '\\': + retval.append("\\\\"); + continue; + default: + if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { + String s = "0000" + Integer.toString(ch, 16); + retval.append("\\u" + s.substring(s.length() - 4, s.length())); + } else { + retval.append(ch); + } + continue; + } + } + return retval.toString(); + } + +}
diff --git a/impl/src/main/java/com/sun/el/parser/SimpleCharStream.java b/impl/src/main/java/com/sun/el/parser/SimpleCharStream.java new file mode 100644 index 0000000..4b0b619 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/SimpleCharStream.java
@@ -0,0 +1,436 @@ +/* + * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +/* Generated By:JavaCC: Do not edit this line. SimpleCharStream.java Version 5.0 */ +/* JavaCCOptions:STATIC=false,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ +package com.sun.el.parser; + +/** + * An implementation of interface CharStream, where the stream is assumed to contain only ASCII characters (without + * unicode processing). + */ + +public class SimpleCharStream { + /** Whether parser is static. */ + public static final boolean staticFlag = false; + int bufsize; + int available; + int tokenBegin; + /** Position in buffer. */ + public int bufpos = -1; + protected int bufline[]; + protected int bufcolumn[]; + + protected int column = 0; + protected int line = 1; + + protected boolean prevCharIsCR = false; + protected boolean prevCharIsLF = false; + + protected java.io.Reader inputStream; + + protected char[] buffer; + protected int maxNextCharInd = 0; + protected int inBuf = 0; + protected int tabSize = 8; + + protected void setTabSize(int i) { + tabSize = i; + } + + protected int getTabSize(int i) { + return tabSize; + } + + protected void ExpandBuff(boolean wrapAround) { + char[] newbuffer = new char[bufsize + 2048]; + int newbufline[] = new int[bufsize + 2048]; + int newbufcolumn[] = new int[bufsize + 2048]; + + try { + if (wrapAround) { + System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); + System.arraycopy(buffer, 0, newbuffer, bufsize - tokenBegin, bufpos); + buffer = newbuffer; + + System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); + System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos); + bufline = newbufline; + + System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); + System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos); + bufcolumn = newbufcolumn; + + maxNextCharInd = (bufpos += (bufsize - tokenBegin)); + } else { + System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin); + buffer = newbuffer; + + System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin); + bufline = newbufline; + + System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin); + bufcolumn = newbufcolumn; + + maxNextCharInd = (bufpos -= tokenBegin); + } + } catch (Throwable t) { + throw new Error(t.getMessage()); + } + + bufsize += 2048; + available = bufsize; + tokenBegin = 0; + } + + protected void FillBuff() throws java.io.IOException { + if (maxNextCharInd == available) { + if (available == bufsize) { + if (tokenBegin > 2048) { + bufpos = maxNextCharInd = 0; + available = tokenBegin; + } else if (tokenBegin < 0) { + bufpos = maxNextCharInd = 0; + } else { + ExpandBuff(false); + } + } else if (available > tokenBegin) { + available = bufsize; + } else if ((tokenBegin - available) < 2048) { + ExpandBuff(true); + } else { + available = tokenBegin; + } + } + + int i; + try { + if ((i = inputStream.read(buffer, maxNextCharInd, available - maxNextCharInd)) == -1) { + inputStream.close(); + throw new java.io.IOException(); + } else { + maxNextCharInd += i; + } + return; + } catch (java.io.IOException e) { + --bufpos; + backup(0); + if (tokenBegin == -1) { + tokenBegin = bufpos; + } + throw e; + } + } + + /** Start. */ + public char BeginToken() throws java.io.IOException { + tokenBegin = -1; + char c = readChar(); + tokenBegin = bufpos; + + return c; + } + + protected void UpdateLineColumn(char c) { + column++; + + if (prevCharIsLF) { + prevCharIsLF = false; + line += (column = 1); + } else if (prevCharIsCR) { + prevCharIsCR = false; + if (c == '\n') { + prevCharIsLF = true; + } else { + line += (column = 1); + } + } + + switch (c) { + case '\r': + prevCharIsCR = true; + break; + case '\n': + prevCharIsLF = true; + break; + case '\t': + column--; + column += (tabSize - (column % tabSize)); + break; + default: + break; + } + + bufline[bufpos] = line; + bufcolumn[bufpos] = column; + } + + /** Read a character. */ + public char readChar() throws java.io.IOException { + if (inBuf > 0) { + --inBuf; + + if (++bufpos == bufsize) { + bufpos = 0; + } + + return buffer[bufpos]; + } + + if (++bufpos >= maxNextCharInd) { + FillBuff(); + } + + char c = buffer[bufpos]; + + UpdateLineColumn(c); + return c; + } + + @Deprecated + /** + * @deprecated + * @see #getEndColumn + */ + + public int getColumn() { + return bufcolumn[bufpos]; + } + + @Deprecated + /** + * @deprecated + * @see #getEndLine + */ + + public int getLine() { + return bufline[bufpos]; + } + + /** Get token end column number. */ + public int getEndColumn() { + return bufcolumn[bufpos]; + } + + /** Get token end line number. */ + public int getEndLine() { + return bufline[bufpos]; + } + + /** Get token beginning column number. */ + public int getBeginColumn() { + return bufcolumn[tokenBegin]; + } + + /** Get token beginning line number. */ + public int getBeginLine() { + return bufline[tokenBegin]; + } + + /** Backup a number of characters. */ + public void backup(int amount) { + + inBuf += amount; + if ((bufpos -= amount) < 0) { + bufpos += bufsize; + } + } + + /** Constructor. */ + public SimpleCharStream(java.io.Reader dstream, int startline, int startcolumn, int buffersize) { + inputStream = dstream; + line = startline; + column = startcolumn - 1; + + available = bufsize = buffersize; + buffer = new char[buffersize]; + bufline = new int[buffersize]; + bufcolumn = new int[buffersize]; + } + + /** Constructor. */ + public SimpleCharStream(java.io.Reader dstream, int startline, int startcolumn) { + this(dstream, startline, startcolumn, 4096); + } + + /** Constructor. */ + public SimpleCharStream(java.io.Reader dstream) { + this(dstream, 1, 1, 4096); + } + + /** Reinitialise. */ + public void ReInit(java.io.Reader dstream, int startline, int startcolumn, int buffersize) { + inputStream = dstream; + line = startline; + column = startcolumn - 1; + + if (buffer == null || buffersize != buffer.length) { + available = bufsize = buffersize; + buffer = new char[buffersize]; + bufline = new int[buffersize]; + bufcolumn = new int[buffersize]; + } + prevCharIsLF = prevCharIsCR = false; + tokenBegin = inBuf = maxNextCharInd = 0; + bufpos = -1; + } + + /** Reinitialise. */ + public void ReInit(java.io.Reader dstream, int startline, int startcolumn) { + ReInit(dstream, startline, startcolumn, 4096); + } + + /** Reinitialise. */ + public void ReInit(java.io.Reader dstream) { + ReInit(dstream, 1, 1, 4096); + } + + /** Constructor. */ + public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline, int startcolumn, int buffersize) + throws java.io.UnsupportedEncodingException { + this(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize); + } + + /** Constructor. */ + public SimpleCharStream(java.io.InputStream dstream, int startline, int startcolumn, int buffersize) { + this(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize); + } + + /** Constructor. */ + public SimpleCharStream(java.io.InputStream dstream, String encoding, int startline, int startcolumn) throws java.io.UnsupportedEncodingException { + this(dstream, encoding, startline, startcolumn, 4096); + } + + /** Constructor. */ + public SimpleCharStream(java.io.InputStream dstream, int startline, int startcolumn) { + this(dstream, startline, startcolumn, 4096); + } + + /** Constructor. */ + public SimpleCharStream(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException { + this(dstream, encoding, 1, 1, 4096); + } + + /** Constructor. */ + public SimpleCharStream(java.io.InputStream dstream) { + this(dstream, 1, 1, 4096); + } + + /** Reinitialise. */ + public void ReInit(java.io.InputStream dstream, String encoding, int startline, int startcolumn, int buffersize) + throws java.io.UnsupportedEncodingException { + ReInit(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, + buffersize); + } + + /** Reinitialise. */ + public void ReInit(java.io.InputStream dstream, int startline, int startcolumn, int buffersize) { + ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize); + } + + /** Reinitialise. */ + public void ReInit(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException { + ReInit(dstream, encoding, 1, 1, 4096); + } + + /** Reinitialise. */ + public void ReInit(java.io.InputStream dstream) { + ReInit(dstream, 1, 1, 4096); + } + + /** Reinitialise. */ + public void ReInit(java.io.InputStream dstream, String encoding, int startline, int startcolumn) throws java.io.UnsupportedEncodingException { + ReInit(dstream, encoding, startline, startcolumn, 4096); + } + + /** Reinitialise. */ + public void ReInit(java.io.InputStream dstream, int startline, int startcolumn) { + ReInit(dstream, startline, startcolumn, 4096); + } + + /** Get token literal value. */ + public String GetImage() { + if (bufpos >= tokenBegin) { + return new String(buffer, tokenBegin, bufpos - tokenBegin + 1); + } else { + return new String(buffer, tokenBegin, bufsize - tokenBegin) + new String(buffer, 0, bufpos + 1); + } + } + + /** Get the suffix. */ + public char[] GetSuffix(int len) { + char[] ret = new char[len]; + + if ((bufpos + 1) >= len) { + System.arraycopy(buffer, bufpos - len + 1, ret, 0, len); + } else { + System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0, len - bufpos - 1); + System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1); + } + + return ret; + } + + /** Reset buffer when finished. */ + public void Done() { + buffer = null; + bufline = null; + bufcolumn = null; + } + + /** + * Method to adjust line and column numbers for the start of a token. + */ + public void adjustBeginLineColumn(int newLine, int newCol) { + int start = tokenBegin; + int len; + + if (bufpos >= tokenBegin) { + len = bufpos - tokenBegin + inBuf + 1; + } else { + len = bufsize - tokenBegin + bufpos + 1 + inBuf; + } + + int i = 0, j = 0, k = 0; + int nextColDiff = 0, columnDiff = 0; + + while (i < len && bufline[j = start % bufsize] == bufline[k = ++start % bufsize]) { + bufline[j] = newLine; + nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j]; + bufcolumn[j] = newCol + columnDiff; + columnDiff = nextColDiff; + i++; + } + + if (i < len) { + bufline[j] = newLine++; + bufcolumn[j] = newCol + columnDiff; + + while (i++ < len) { + if (bufline[j = start % bufsize] != bufline[++start % bufsize]) { + bufline[j] = newLine++; + } else { + bufline[j] = newLine; + } + } + } + + line = bufline[j]; + column = bufcolumn[j]; + } + +} +/* JavaCC - OriginalChecksum=7ea14199259e7ce0336b228c8cdb9958 (do not edit this line) */
diff --git a/impl/src/main/java/com/sun/el/parser/SimpleNode.java b/impl/src/main/java/com/sun/el/parser/SimpleNode.java new file mode 100644 index 0000000..1c004c6 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/SimpleNode.java
@@ -0,0 +1,229 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import javax.el.ELException; +import javax.el.MethodInfo; +import javax.el.PropertyNotWritableException; +import javax.el.ValueReference; + +import com.sun.el.lang.ELSupport; +import com.sun.el.lang.EvaluationContext; +import com.sun.el.util.MessageFactory; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public abstract class SimpleNode extends ELSupport implements Node { + protected Node parent; + + protected Node[] children; + + protected int id; + + protected String image; + + public SimpleNode(int i) { + id = i; + } + + @Override + public void jjtOpen() { + } + + @Override + public void jjtClose() { + } + + @Override + public void jjtSetParent(Node n) { + parent = n; + } + + @Override + public Node jjtGetParent() { + return parent; + } + + @Override + public void jjtAddChild(Node n, int i) { + if (children == null) { + children = new Node[i + 1]; + } else if (i >= children.length) { + Node c[] = new Node[i + 1]; + System.arraycopy(children, 0, c, 0, children.length); + children = c; + } + children[i] = n; + } + + @Override + public Node jjtGetChild(int i) { + return children[i]; + } + + @Override + public int jjtGetNumChildren() { + return (children == null) ? 0 : children.length; + } + + /* + * You can override these two methods in subclasses of SimpleNode to customize the way the node appears when the tree is + * dumped. If your output uses more than one line you should override toString(String), otherwise overriding toString() + * is probably all you need to do. + */ + + @Override + public String toString() { + if (this.image != null) { + return ELParserTreeConstants.jjtNodeName[id] + "[" + this.image + "]"; + } + return ELParserTreeConstants.jjtNodeName[id]; + } + + public String toString(String prefix) { + return prefix + toString(); + } + + /* + * Override this method if you want to customize how the node dumps out its children. + */ + + public void dump(String prefix) { + System.out.println(toString(prefix)); + if (children != null) { + for (int i = 0; i < children.length; ++i) { + SimpleNode n = (SimpleNode) children[i]; + if (n != null) { + n.dump(prefix + " "); + } + } + } + } + + @Override + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } + + @Override + public Class getType(EvaluationContext ctx) throws ELException { + throw new UnsupportedOperationException(); + } + + @Override + public Object getValue(EvaluationContext ctx) throws ELException { + throw new UnsupportedOperationException(); + } + + @Override + public ValueReference getValueReference(EvaluationContext ctx) throws ELException { + return null; + } + + @Override + public boolean isReadOnly(EvaluationContext ctx) throws ELException { + return true; + } + + @Override + public void setValue(EvaluationContext ctx, Object value) throws ELException { + throw new PropertyNotWritableException(MessageFactory.get("error.syntax.set")); + } + + @Override + public void accept(NodeVisitor visitor) throws ELException { + visitor.visit(this); + if (this.children != null && this.children.length > 0) { + for (int i = 0; i < this.children.length; i++) { + this.children[i].accept(visitor); + } + } + } + + @Override + public Object invoke(EvaluationContext ctx, Class[] paramTypes, Object[] paramValues) throws ELException { + throw new UnsupportedOperationException(); + } + + @Override + public MethodInfo getMethodInfo(EvaluationContext ctx, Class[] paramTypes) throws ELException { + throw new UnsupportedOperationException(); + } + + @Override + public boolean equals(Object node) { + if (!(node instanceof SimpleNode)) { + return false; + } + SimpleNode n = (SimpleNode) node; + if (this.id != n.id) { + return false; + } + if (this.children == null && n.children == null) { + if (this.image == null) { + return n.image == null; + } + return this.image.equals(n.image); + } + if (this.children == null || n.children == null) { + // One is null and the other is non-null + return false; + } + if (this.children.length != n.children.length) { + return false; + } + if (this.children.length == 0) { + if (this.image == null) { + return n.image == null; + } + return this.image.equals(n.image); + } + for (int i = 0; i < this.children.length; i++) { + if (!this.children[i].equals(n.children[i])) { + return false; + } + } + return true; + } + + @Override + public boolean isParametersProvided() { + return false; + } + + @Override + public int hashCode() { + if (this.children == null || this.children.length == 0) { + if (this.image != null) { + return this.image.hashCode(); + } + return this.id; + } + int h = 0; + for (int i = this.children.length - 1; i >= 0; i--) { + h = h + h + h + this.children[i].hashCode(); + } + h = h + h + h + id; + return h; + } +}
diff --git a/impl/src/main/java/com/sun/el/parser/Token.java b/impl/src/main/java/com/sun/el/parser/Token.java new file mode 100644 index 0000000..54eab3e --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/Token.java
@@ -0,0 +1,86 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +import java.io.Serializable; + +/** + * Describes the input token stream. + */ + +public class Token implements Serializable { + + /** + * An integer that describes the kind of this token. This numbering system is determined by JavaCCParser, and a table of + * these numbers is stored in the file ...Constants.java. + */ + public int kind; + + /** + * beginLine and beginColumn describe the position of the first character of this token; endLine and endColumn describe + * the position of the last character of this token. + */ + public int beginLine, beginColumn, endLine, endColumn; + + /** + * The string image of the token. + */ + public String image; + + /** + * A reference to the next regular (non-special) token from the input stream. If this is the last token from the input + * stream, or if the token manager has not read tokens beyond this one, this field is set to null. This is true only if + * this token is also a regular token. Otherwise, see below for a description of the contents of this field. + */ + public Token next; + + /** + * This field is used to access special tokens that occur prior to this token, but after the immediately preceding + * regular (non-special) token. If there are no such special tokens, this field is set to null. When there are more than + * one such special token, this field refers to the last of these special tokens, which in turn refers to the next + * previous special token through its specialToken field, and so on until the first special token (whose specialToken + * field is null). The next fields of special tokens refer to other special tokens that immediately follow it (without + * an intervening regular token). If there is no such token, this field is null. + */ + public Token specialToken; + + /** + * Returns the image. + */ + @Override + public String toString() { + return image; + } + + /** + * Returns a new Token object, by default. However, if you want, you can create and return subclass objects based on the + * value of ofKind. Simply add the cases to the switch for all those special cases. For example, if you have a subclass + * of Token called IDToken that you want to create if ofKind is ID, simlpy add something like : + * + * case MyParserConstants.ID : return new IDToken(); + * + * to the following switch statement. Then you can cast matchedToken variable to the appropriate type and use it in your + * lexical actions. + */ + public static final Token newToken(int ofKind) { + switch (ofKind) { + default: + return new Token(); + } + } + +}
diff --git a/impl/src/main/java/com/sun/el/parser/TokenMgrError.java b/impl/src/main/java/com/sun/el/parser/TokenMgrError.java new file mode 100644 index 0000000..ae39207 --- /dev/null +++ b/impl/src/main/java/com/sun/el/parser/TokenMgrError.java
@@ -0,0 +1,137 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.parser; + +public class TokenMgrError extends Error { + /* + * Ordinals for various reasons why an Error of this type can be thrown. + */ + + /** + * Lexical error occured. + */ + static final int LEXICAL_ERROR = 0; + + /** + * An attempt wass made to create a second instance of a static token manager. + */ + static final int STATIC_LEXER_ERROR = 1; + + /** + * Tried to change to an invalid lexical state. + */ + static final int INVALID_LEXICAL_STATE = 2; + + /** + * Detected (and bailed out of) an infinite loop in the token manager. + */ + static final int LOOP_DETECTED = 3; + + /** + * Indicates the reason why the exception is thrown. It will have one of the above 4 values. + */ + int errorCode; + + /** + * Replaces unprintable characters by their espaced (or unicode escaped) equivalents in the given string + */ + protected static final String addEscapes(String str) { + StringBuffer retval = new StringBuffer(); + char ch; + for (int i = 0; i < str.length(); i++) { + switch (str.charAt(i)) { + case 0: + continue; + case '\b': + retval.append("\\b"); + continue; + case '\t': + retval.append("\\t"); + continue; + case '\n': + retval.append("\\n"); + continue; + case '\f': + retval.append("\\f"); + continue; + case '\r': + retval.append("\\r"); + continue; + case '\"': + retval.append("\\\""); + continue; + case '\'': + retval.append("\\\'"); + continue; + case '\\': + retval.append("\\\\"); + continue; + default: + if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { + String s = "0000" + Integer.toString(ch, 16); + retval.append("\\u" + s.substring(s.length() - 4, s.length())); + } else { + retval.append(ch); + } + continue; + } + } + return retval.toString(); + } + + /** + * Returns a detailed message for the Error when it is thrown by the token manager to indicate a lexical error. + * Parameters : EOFSeen : indicates if EOF caused the lexicl error curLexState : lexical state in which this error + * occured errorLine : line number when the error occured errorColumn : column number when the error occured errorAfter + * : prefix that was seen before this error occured curchar : the offending character Note: You can customize the + * lexical error message by modifying this method. + */ + protected static String LexicalError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar) { + return ("Lexical error at line " + errorLine + ", column " + errorColumn + ". Encountered: " + + (EOFSeen ? "<EOF> " : ("\"" + addEscapes(String.valueOf(curChar)) + "\"") + " (" + (int) curChar + "), ") + "after : \"" + + addEscapes(errorAfter) + "\""); + } + + /** + * You can also modify the body of this method to customize your error messages. For example, cases like LOOP_DETECTED + * and INVALID_LEXICAL_STATE are not of end-users concern, so you can return something like : + * + * "Internal Error : Please file a bug report .... " + * + * from this method for such cases in the release version of your parser. + */ + @Override + public String getMessage() { + return super.getMessage(); + } + + /* + * Constructors of various flavors follow. + */ + + public TokenMgrError() { + } + + public TokenMgrError(String message, int reason) { + super(message); + errorCode = reason; + } + + public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar, int reason) { + this(LexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason); + } +}
diff --git a/impl/src/main/java/com/sun/el/stream/Operator.java b/impl/src/main/java/com/sun/el/stream/Operator.java new file mode 100644 index 0000000..4793ce3 --- /dev/null +++ b/impl/src/main/java/com/sun/el/stream/Operator.java
@@ -0,0 +1,24 @@ +/* + * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.stream; + +import java.util.Iterator; + +interface Operator { + + Iterator<Object> iterator(Iterator<Object> upstream); +}
diff --git a/impl/src/main/java/com/sun/el/stream/Optional.java b/impl/src/main/java/com/sun/el/stream/Optional.java new file mode 100644 index 0000000..2ea52b6 --- /dev/null +++ b/impl/src/main/java/com/sun/el/stream/Optional.java
@@ -0,0 +1,61 @@ +/* + * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.stream; + +import javax.el.LambdaExpression; + +public class Optional { + + private final static Optional EMPTY = new Optional(); + private final Object value; + + Optional(Object value) { + if (value == null) { + throw new NullPointerException(); + } + this.value = value; + } + + Optional() { + this.value = null; + } + + public boolean isPresent() { + return value != null; + } + + public void ifPresent(LambdaExpression lambda) { + if (value != null) { + lambda.invoke(value); + } + } + + public Object get() { + if (value == null) { + throw new java.util.NoSuchElementException("No value present"); + } + return value; + } + + public Object orElse(Object other) { + return value != null ? value : other; + } + + public Object orElseGet(LambdaExpression other) { + return value != null ? value : other.invoke(); + } +}
diff --git a/impl/src/main/java/com/sun/el/stream/Stream.java b/impl/src/main/java/com/sun/el/stream/Stream.java new file mode 100644 index 0000000..9091271 --- /dev/null +++ b/impl/src/main/java/com/sun/el/stream/Stream.java
@@ -0,0 +1,526 @@ +/* + * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.stream; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Iterator; +import java.util.PriorityQueue; +import java.util.Set; + +import javax.el.ELException; +import javax.el.LambdaExpression; + +import com.sun.el.lang.ELArithmetic; +import com.sun.el.lang.ELSupport; + +/* + */ + +public class Stream { + + private Iterator<Object> source; + private Stream upstream; + private Operator op; + + Stream(Iterator<Object> source) { + this.source = source; + } + + Stream(Stream upstream, Operator op) { + this.upstream = upstream; + this.op = op; + } + + public Iterator<Object> iterator() { + if (source != null) { + return source; + } + + return op.iterator(upstream.iterator()); + } + + public Stream filter(final LambdaExpression predicate) { + return new Stream(this, new Operator() { + @Override + public Iterator<Object> iterator(final Iterator<Object> upstream) { + return new Iterator2(upstream) { + @Override + public void doItem(Object item) { + if ((Boolean) predicate.invoke(item)) { + yield(item); + } + } + }; + } + }); + } + + public Stream map(final LambdaExpression mapper) { + return new Stream(this, new Operator() { + @Override + public Iterator<Object> iterator(final Iterator<Object> up) { + return new Iterator1(up) { + @Override + public Object next() { + return mapper.invoke(iter.next()); + } + }; + } + }); + } + + public Stream peek(final LambdaExpression comsumer) { + return new Stream(this, new Operator() { + @Override + public Iterator<Object> iterator(final Iterator<Object> up) { + return new Iterator2(up) { + @Override + void doItem(Object item) { + comsumer.invoke(item); + yield(item); + } + }; + } + }); + } + + public Stream limit(final long n) { + if (n < 0) { + throw new IllegalArgumentException("limit must be non-negative"); + } + return new Stream(this, new Operator() { + @Override + public Iterator<Object> iterator(final Iterator<Object> up) { + return new Iterator0() { + long limit = n; + + @Override + public boolean hasNext() { + return (limit > 0) ? up.hasNext() : false; + } + + @Override + public Object next() { + limit--; + return up.next(); + } + }; + } + }); + } + + public Stream substream(final long startIndex) { + if (startIndex < 0) { + throw new IllegalArgumentException("substream index must be non-negative"); + } + return new Stream(this, new Operator() { + long skip = startIndex; + + @Override + public Iterator<Object> iterator(final Iterator<Object> up) { + while (skip > 0 && up.hasNext()) { + up.next(); + skip--; + } + return up; + } + }); + } + + public Stream substream(long startIndex, long endIndex) { + return substream(startIndex).limit(endIndex - startIndex); + } + + public Stream distinct() { + return new Stream(this, new Operator() { + @Override + public Iterator<Object> iterator(final Iterator<Object> up) { + return new Iterator2(up) { + private Set<Object> set = new HashSet<Object>(); + + @Override + public void doItem(Object item) { + if (set.add(item)) { + yield(item); + } + } + }; + } + }); + } + + public Stream sorted() { + return new Stream(this, new Operator() { + + private PriorityQueue<Object> queue = null; + + @Override + public Iterator<Object> iterator(final Iterator<Object> up) { + if (queue == null) { + queue = new PriorityQueue<Object>(16, new Comparator<Object>() { + @Override + public int compare(Object o1, Object o2) { + return ((Comparable) o1).compareTo(o2); + } + }); + + while (up.hasNext()) { + queue.add(up.next()); + } + } + + return new Iterator0() { + @Override + public boolean hasNext() { + return !queue.isEmpty(); + } + + @Override + public Object next() { + return queue.remove(); + } + }; + } + }); + } + + public Stream sorted(final LambdaExpression comparator) { + return new Stream(this, new Operator() { + + private PriorityQueue<Object> queue = null; + + @Override + public Iterator<Object> iterator(final Iterator<Object> up) { + if (queue == null) { + queue = new PriorityQueue<Object>(16, new Comparator<Object>() { + @Override + public int compare(Object o1, Object o2) { + return (Integer) ELSupport.coerceToType(comparator.invoke(o1, o2), Integer.class); + } + }); + + while (up.hasNext()) { + queue.add(up.next()); + } + } + + return new Iterator0() { + @Override + public boolean hasNext() { + return !queue.isEmpty(); + } + + @Override + public Object next() { + return queue.remove(); + } + }; + } + }); + } + + public Stream flatMap(final LambdaExpression mapper) { + return new Stream(this, new Operator() { + @Override + public Iterator<Object> iterator(final Iterator<Object> upstream) { + return new Iterator0() { + Iterator<Object> iter = null; + + @Override + public boolean hasNext() { + while (true) { + if (iter == null) { + if (!upstream.hasNext()) { + return false; + } + Object mapped = mapper.invoke(upstream.next()); + if (!(mapped instanceof Stream)) { + throw new ELException("Expecting a Stream " + "from flatMap's mapper function."); + } + iter = ((Stream) mapped).iterator(); + } else { + if (iter.hasNext()) { + return true; + } + iter = null; + } + } + } + + @Override + public Object next() { + if (iter == null) { + return null; + } + return iter.next(); + } + }; + } + }); + } + + public Object reduce(Object base, LambdaExpression op) { + Iterator<Object> iter = iterator(); + while (iter.hasNext()) { + base = op.invoke(base, iter.next()); + } + return base; + } + + public Optional reduce(LambdaExpression op) { + Iterator<Object> iter = iterator(); + if (iter.hasNext()) { + Object base = iter.next(); + while (iter.hasNext()) { + base = op.invoke(base, iter.next()); + } + return new Optional(base); + } + return new Optional(); + } + + /* + * public Map<Object,Object> reduceBy(LambdaExpression classifier, LambdaExpression seed, LambdaExpression reducer) { + * Map<Object,Object> map = new HashMap<Object,Object>(); Iterator<Object> iter = iterator(); while (iter.hasNext()) { + * Object item = iter.next(); Object key = classifier.invoke(item); Object value = map.get(key); if (value == null) { + * value = seed.invoke(); } map.put(key, reducer.invoke(value, item)); } return map; } + */ + + public void forEach(LambdaExpression comsumer) { + Iterator<Object> iter = iterator(); + while (iter.hasNext()) { + comsumer.invoke(iter.next()); + } + } + + /* + * public Map<Object,Collection<Object>> groupBy(LambdaExpression classifier) { Map<Object, Collection<Object>> map = + * new HashMap<Object, Collection<Object>>(); Iterator<Object> iter = iterator(); while (iter.hasNext()) { Object item = + * iter.next(); Object key = classifier.invoke(item); if (key == null) { throw new ELException("null key"); } + * Collection<Object> c = map.get(key); if (c == null) { c = new ArrayList<Object>(); map.put(key, c); } c.add(item); } + * return map; } + */ + public boolean anyMatch(LambdaExpression predicate) { + Iterator<Object> iter = iterator(); + while (iter.hasNext()) { + if ((Boolean) predicate.invoke(iter.next())) { + return true; + } + } + return false; + } + + public boolean allMatch(LambdaExpression predicate) { + Iterator<Object> iter = iterator(); + while (iter.hasNext()) { + if (!(Boolean) predicate.invoke(iter.next())) { + return false; + } + } + return true; + } + + public boolean noneMatch(LambdaExpression predicate) { + Iterator<Object> iter = iterator(); + while (iter.hasNext()) { + if ((Boolean) predicate.invoke(iter.next())) { + return false; + } + } + return true; + } + + public Object[] toArray() { + Iterator<Object> iter = iterator(); + ArrayList<Object> al = new ArrayList<Object>(); + while (iter.hasNext()) { + al.add(iter.next()); + } + return al.toArray(); + } + + public Object toList() { + Iterator<Object> iter = iterator(); + ArrayList<Object> al = new ArrayList<Object>(); + while (iter.hasNext()) { + al.add(iter.next()); + } + return al; + } + + /* + * public Object into(Object target) { if (! (target instanceof Collection)) { throw new + * ELException("The argument type for into operation mush be a Collection"); } Collection<Object> c = + * (Collection<Object>) target; Iterator<Object> iter = iterator(); while (iter.hasNext()) { c.add(iter.next()); } + * return c; } + */ + + public Optional findFirst() { + Iterator<Object> iter = iterator(); + if (iter.hasNext()) { + return new Optional(iter.next()); + } else { + return new Optional(); + } + } + + public Object sum() { + Number sum = Long.valueOf(0); + Iterator<Object> iter = iterator(); + while (iter.hasNext()) { + sum = ELArithmetic.add(sum, iter.next()); + } + return sum; + } + + public Object count() { + long count = 0; + Iterator<Object> iter = iterator(); + while (iter.hasNext()) { + count++; + iter.next(); + } + return Long.valueOf(count); + } + + public Optional min() { + Object min = null; + Iterator<Object> iter = iterator(); + while (iter.hasNext()) { + Object item = iter.next(); + if (min == null || ELSupport.compare(min, item) > 0) { + min = item; + } + } + if (min == null) { + return new Optional(); + } + return new Optional(min); + } + + public Optional max() { + Object max = null; + Iterator<Object> iter = iterator(); + while (iter.hasNext()) { + Object item = iter.next(); + if (max == null || ELSupport.compare(max, item) < 0) { + max = item; + } + } + if (max == null) { + return new Optional(); + } + return new Optional(max); + } + + public Optional min(final LambdaExpression comparator) { + Object min = null; + Iterator<Object> iter = iterator(); + while (iter.hasNext()) { + Object item = iter.next(); + if (min == null || ELSupport.compare(comparator.invoke(item, min), Long.valueOf(0)) < 0) { + min = item; + } + } + if (min == null) { + return new Optional(); + } + return new Optional(min); + } + + public Optional max(final LambdaExpression comparator) { + Object max = null; + Iterator<Object> iter = iterator(); + while (iter.hasNext()) { + Object item = iter.next(); + if (max == null || ELSupport.compare(comparator.invoke(max, item), Long.valueOf(0)) < 0) { + max = item; + } + } + if (max == null) { + return new Optional(); + } + return new Optional(max); + } + + public Optional average() { + Number sum = Long.valueOf(0); + long count = 0; + Iterator<Object> iter = iterator(); + while (iter.hasNext()) { + count++; + sum = ELArithmetic.add(sum, iter.next()); + } + if (count == 0) { + return new Optional(); + } + return new Optional(ELArithmetic.divide(sum, count)); + } + + abstract class Iterator0 implements Iterator<Object> { + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + + abstract class Iterator1 extends Iterator0 { + + Iterator iter; + + Iterator1(Iterator iter) { + this.iter = iter; + } + + @Override + public boolean hasNext() { + return iter.hasNext(); + } + } + + abstract class Iterator2 extends Iterator1 { + private Object current; + private boolean yielded; + + Iterator2(Iterator upstream) { + super(upstream); + } + + @Override + public Object next() { + yielded = false; + return current; + } + + @Override + public boolean hasNext() { + while ((!yielded) && iter.hasNext()) { + doItem(iter.next()); + } + return yielded; + } + + void yield(Object current) { + this.current = current; + yielded = true; + } + + abstract void doItem(Object item); + } +}
diff --git a/impl/src/main/java/com/sun/el/stream/StreamELResolver.java b/impl/src/main/java/com/sun/el/stream/StreamELResolver.java new file mode 100644 index 0000000..0963a55 --- /dev/null +++ b/impl/src/main/java/com/sun/el/stream/StreamELResolver.java
@@ -0,0 +1,120 @@ +/* + * Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.stream; + +import java.beans.FeatureDescriptor; +import java.lang.reflect.Array; +import java.util.Collection; +import java.util.Iterator; + +import javax.el.ELContext; +import javax.el.ELResolver; + +/* + * This ELResolver intercepts method calls to a Collections, to provide + * support for collection operations. + */ + +public class StreamELResolver extends ELResolver { + + @Override + public Object invoke(final ELContext context, final Object base, final Object method, final Class<?>[] paramTypes, final Object[] params) { + + if (context == null) { + throw new NullPointerException(); + } + + if (base instanceof Collection) { + @SuppressWarnings("unchecked") + Collection<Object> c = (Collection<Object>) base; + if ("stream".equals(method) && params.length == 0) { + context.setPropertyResolved(true); + return new Stream(c.iterator()); + } + } + if (base.getClass().isArray()) { + if ("stream".equals(method) && params.length == 0) { + context.setPropertyResolved(true); + return new Stream(arrayIterator(base)); + } + } + return null; + } + + private static Iterator<Object> arrayIterator(final Object base) { + final int size = Array.getLength(base); + return new Iterator<Object>() { + int index = 0; + boolean yielded; + Object current; + + @Override + public boolean hasNext() { + if ((!yielded) && index < size) { + current = Array.get(base, index++); + yielded = true; + } + return yielded; + } + + @Override + public Object next() { + yielded = false; + return current; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + /* + * private LambdaExpression getLambda(Object obj, String method) { if (obj == null || ! (obj instanceof + * LambdaExpression)) { throw new ELException ("When calling " + method + ", expecting an " + + * "EL lambda expression, but found " + obj); } return (LambdaExpression) obj; } + */ + @Override + public Object getValue(ELContext context, Object base, Object property) { + return null; + } + + @Override + public Class<?> getType(ELContext context, Object base, Object property) { + return null; + } + + @Override + public void setValue(ELContext context, Object base, Object property, Object value) { + } + + @Override + public boolean isReadOnly(ELContext context, Object base, Object property) { + return false; + } + + @Override + public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) { + return null; + } + + @Override + public Class<?> getCommonPropertyType(ELContext context, Object base) { + return String.class; + } +}
diff --git a/impl/src/main/java/com/sun/el/util/MessageFactory.java b/impl/src/main/java/com/sun/el/util/MessageFactory.java new file mode 100644 index 0000000..356124b --- /dev/null +++ b/impl/src/main/java/com/sun/el/util/MessageFactory.java
@@ -0,0 +1,65 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.util; + +import java.text.MessageFormat; +import java.util.ResourceBundle; + +/** + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public final class MessageFactory { + + protected final static ResourceBundle bundle = ResourceBundle.getBundle("com.sun.el.Messages"); + + /** + * + */ + public MessageFactory() { + super(); + } + + public static String get(final String key) { + return bundle.getString(key); + } + + public static String get(final String key, final Object obj0) { + return getArray(key, new Object[] { obj0 }); + } + + public static String get(final String key, final Object obj0, final Object obj1) { + return getArray(key, new Object[] { obj0, obj1 }); + } + + public static String get(final String key, final Object obj0, final Object obj1, final Object obj2) { + return getArray(key, new Object[] { obj0, obj1, obj2 }); + } + + public static String get(final String key, final Object obj0, final Object obj1, final Object obj2, final Object obj3) { + return getArray(key, new Object[] { obj0, obj1, obj2, obj3 }); + } + + public static String get(final String key, final Object obj0, final Object obj1, final Object obj2, final Object obj3, final Object obj4) { + return getArray(key, new Object[] { obj0, obj1, obj2, obj3, obj4 }); + } + + public static String getArray(final String key, final Object[] objA) { + return MessageFormat.format(bundle.getString(key), objA); + } + +}
diff --git a/impl/src/main/java/com/sun/el/util/ReflectionUtil.java b/impl/src/main/java/com/sun/el/util/ReflectionUtil.java new file mode 100644 index 0000000..3e4e48e --- /dev/null +++ b/impl/src/main/java/com/sun/el/util/ReflectionUtil.java
@@ -0,0 +1,712 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package com.sun.el.util; + +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import javax.el.ELContext; +import javax.el.ELException; +import javax.el.MethodNotFoundException; +import javax.el.PropertyNotFoundException; + +import com.sun.el.lang.ELSupport; + +/** + * Utilities for Managing Serialization and Reflection + * + * @author Jacob Hookom [jacob@hookom.net] + * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $ + */ +public class ReflectionUtil { + + protected static final String[] EMPTY_STRING = new String[0]; + + protected static final String[] PRIMITIVE_NAMES = new String[] { "boolean", "byte", "char", "double", "float", "int", "long", "short", "void" }; + + protected static final Class[] PRIMITIVES = new Class[] { boolean.class, byte.class, char.class, double.class, float.class, int.class, long.class, + short.class, Void.TYPE }; + + /** + * + */ + private ReflectionUtil() { + super(); + } + + public static Class forName(String name) throws ClassNotFoundException { + if (null == name || "".equals(name)) { + return null; + } + Class c = forNamePrimitive(name); + if (c == null) { + if (name.endsWith("[]")) { + String nc = name.substring(0, name.length() - 2); + c = Class.forName(nc, true, Thread.currentThread().getContextClassLoader()); + c = Array.newInstance(c, 0).getClass(); + } else { + c = Class.forName(name, true, Thread.currentThread().getContextClassLoader()); + } + } + return c; + } + + protected static Class forNamePrimitive(String name) { + if (name.length() <= 8) { + int p = Arrays.binarySearch(PRIMITIVE_NAMES, name); + if (p >= 0) { + return PRIMITIVES[p]; + } + } + return null; + } + + /** + * Converts an array of Class names to Class types + * + * @param s + * @return The array of Classes + * @throws ClassNotFoundException + */ + public static Class[] toTypeArray(String[] s) throws ClassNotFoundException { + if (s == null) { + return null; + } + Class[] c = new Class[s.length]; + for (int i = 0; i < s.length; i++) { + c[i] = forName(s[i]); + } + return c; + } + + /** + * Converts an array of Class types to Class names + * + * @param c + * @return The array of Classes + */ + public static String[] toTypeNameArray(Class[] c) { + if (c == null) { + return null; + } + String[] s = new String[c.length]; + for (int i = 0; i < c.length; i++) { + s[i] = c[i].getName(); + } + return s; + } + + /** + * @param base The base object + * @param property The property + * @return The PropertyDescriptor for the base with the given property + * @throws ELException + * @throws PropertyNotFoundException + */ + public static PropertyDescriptor getPropertyDescriptor(Object base, Object property) throws ELException, PropertyNotFoundException { + String name = ELSupport.coerceToString(property); + PropertyDescriptor p = null; + try { + PropertyDescriptor[] desc = Introspector.getBeanInfo(base.getClass()).getPropertyDescriptors(); + for (int i = 0; i < desc.length; i++) { + if (desc[i].getName().equals(name)) { + return desc[i]; + } + } + } catch (IntrospectionException ie) { + throw new ELException(ie); + } + throw new PropertyNotFoundException(MessageFactory.get("error.property.notfound", base, name)); + } + + /* + * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync. + */ + public static Object invokeMethod(ELContext context, Method m, Object base, Object[] params) { + + Object[] parameters = buildParameters(context, m.getParameterTypes(), m.isVarArgs(), params); + try { + return m.invoke(base, parameters); + } catch (IllegalAccessException iae) { + throw new ELException(iae); + } catch (IllegalArgumentException iae) { + throw new ELException(iae); + } catch (InvocationTargetException ite) { + throw new ELException(ite.getCause()); + } + } + + /* + * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync. + */ + public static Method findMethod(Class<?> clazz, String methodName, Class<?>[] paramTypes, Object[] paramValues) { + + if (clazz == null || methodName == null) { + throw new MethodNotFoundException(MessageFactory.get("error.method.notfound", clazz, methodName, paramString(paramTypes))); + } + + if (paramTypes == null) { + paramTypes = getTypesFromValues(paramValues); + } + + Method[] methods = clazz.getMethods(); + + List<Wrapper> wrappers = Wrapper.wrap(methods, methodName); + + Wrapper result = findWrapper(clazz, wrappers, methodName, paramTypes, paramValues); + + if (result == null) { + return null; + } + return getMethod(clazz, (Method) result.unWrap()); + } + + /* + * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync. + */ + private static Wrapper findWrapper(Class<?> clazz, List<Wrapper> wrappers, String name, Class<?>[] paramTypes, Object[] paramValues) { + + List<Wrapper> assignableCandidates = new ArrayList<Wrapper>(); + List<Wrapper> coercibleCandidates = new ArrayList<Wrapper>(); + List<Wrapper> varArgsCandidates = new ArrayList<Wrapper>(); + + int paramCount; + if (paramTypes == null) { + paramCount = 0; + } else { + paramCount = paramTypes.length; + } + + for (Wrapper w : wrappers) { + Class<?>[] mParamTypes = w.getParameterTypes(); + int mParamCount; + if (mParamTypes == null) { + mParamCount = 0; + } else { + mParamCount = mParamTypes.length; + } + + // Check the number of parameters + if (!(paramCount == mParamCount || (w.isVarArgs() && paramCount >= mParamCount - 1))) { + // Method has wrong number of parameters + continue; + } + + // Check the parameters match + boolean assignable = false; + boolean coercible = false; + boolean varArgs = false; + boolean noMatch = false; + for (int i = 0; i < mParamCount; i++) { + if (i == (mParamCount - 1) && w.isVarArgs()) { + varArgs = true; + // exact var array type match + if (mParamCount == paramCount) { + if (mParamTypes[i] == paramTypes[i]) { + continue; + } + } + + // unwrap the array's component type + Class<?> varType = mParamTypes[i].getComponentType(); + for (int j = i; j < paramCount; j++) { + if (!isAssignableFrom(paramTypes[j], varType) + && !(paramValues != null && j < paramValues.length && isCoercibleFrom(paramValues[j], varType))) { + noMatch = true; + break; + } + } + } else if (mParamTypes[i].equals(paramTypes[i])) { + } else if (isAssignableFrom(paramTypes[i], mParamTypes[i])) { + assignable = true; + } else { + if (paramValues == null || i >= paramValues.length) { + noMatch = true; + break; + } else { + if (isCoercibleFrom(paramValues[i], mParamTypes[i])) { + coercible = true; + } else { + noMatch = true; + break; + } + } + } + } + if (noMatch) { + continue; + } + + if (varArgs) { + varArgsCandidates.add(w); + } else if (coercible) { + coercibleCandidates.add(w); + } else if (assignable) { + assignableCandidates.add(w); + } else { + // If a method is found where every parameter matches exactly, + // return it + return w; + } + + } + + String errorMsg = MessageFactory.get("error.method.ambiguous", clazz, name, paramString(paramTypes)); + if (!assignableCandidates.isEmpty()) { + return findMostSpecificWrapper(assignableCandidates, paramTypes, false, errorMsg); + } else if (!coercibleCandidates.isEmpty()) { + return findMostSpecificWrapper(coercibleCandidates, paramTypes, true, errorMsg); + } else if (!varArgsCandidates.isEmpty()) { + return findMostSpecificWrapper(varArgsCandidates, paramTypes, true, errorMsg); + } else { + throw new MethodNotFoundException(MessageFactory.get("error.method.notfound", clazz, name, paramString(paramTypes))); + } + + } + + /* + * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync. + */ + private static Wrapper findMostSpecificWrapper(List<Wrapper> candidates, Class<?>[] matchingTypes, boolean elSpecific, String errorMsg) { + List<Wrapper> ambiguouses = new ArrayList<Wrapper>(); + for (Wrapper candidate : candidates) { + boolean lessSpecific = false; + + Iterator<Wrapper> it = ambiguouses.iterator(); + while (it.hasNext()) { + int result = isMoreSpecific(candidate, it.next(), matchingTypes, elSpecific); + if (result == 1) { + it.remove(); + } else if (result == -1) { + lessSpecific = true; + } + } + + if (!lessSpecific) { + ambiguouses.add(candidate); + } + } + + if (ambiguouses.size() > 1) { + throw new MethodNotFoundException(errorMsg); + } + + return ambiguouses.get(0); + } + + /* + * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync. + */ + private static int isMoreSpecific(Wrapper wrapper1, Wrapper wrapper2, Class<?>[] matchingTypes, boolean elSpecific) { + Class<?>[] paramTypes1 = wrapper1.getParameterTypes(); + Class<?>[] paramTypes2 = wrapper2.getParameterTypes(); + + if (wrapper1.isVarArgs()) { + // JLS8 15.12.2.5 Choosing the Most Specific Method + int length = Math.max(Math.max(paramTypes1.length, paramTypes2.length), matchingTypes.length); + paramTypes1 = getComparingParamTypesForVarArgsMethod(paramTypes1, length); + paramTypes2 = getComparingParamTypesForVarArgsMethod(paramTypes2, length); + + if (length > matchingTypes.length) { + Class<?>[] matchingTypes2 = new Class<?>[length]; + System.arraycopy(matchingTypes, 0, matchingTypes2, 0, matchingTypes.length); + matchingTypes = matchingTypes2; + } + } + + int result = 0; + for (int i = 0; i < paramTypes1.length; i++) { + if (paramTypes1[i] != paramTypes2[i]) { + int r2 = isMoreSpecific(paramTypes1[i], paramTypes2[i], matchingTypes[i], elSpecific); + if (r2 == 1) { + if (result == -1) { + return 0; + } + result = 1; + } else if (r2 == -1) { + if (result == 1) { + return 0; + } + result = -1; + } else { + return 0; + } + } + } + + if (result == 0) { + // The nature of bridge methods is such that it actually + // doesn't matter which one we pick as long as we pick + // one. That said, pick the 'right' one (the non-bridge + // one) anyway. + result = Boolean.compare(wrapper1.isBridge(), wrapper2.isBridge()); + } + + return result; + } + + /* + * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync. + */ + private static int isMoreSpecific(Class<?> type1, Class<?> type2, Class<?> matchingType, boolean elSpecific) { + type1 = getBoxingTypeIfPrimitive(type1); + type2 = getBoxingTypeIfPrimitive(type2); + if (type2.isAssignableFrom(type1)) { + return 1; + } else if (type1.isAssignableFrom(type2)) { + return -1; + } else { + if (elSpecific) { + /* + * Number will be treated as more specific + * + * ASTInteger only return Long or BigInteger, no Byte / Short / Integer. ASTFloatingPoint also. + * + */ + if (matchingType != null && Number.class.isAssignableFrom(matchingType)) { + boolean b1 = Number.class.isAssignableFrom(type1) || type1.isPrimitive(); + boolean b2 = Number.class.isAssignableFrom(type2) || type2.isPrimitive(); + if (b1 && !b2) { + return 1; + } else if (b2 && !b1) { + return -1; + } else { + return 0; + } + } + + return 0; + } else { + return 0; + } + } + } + + /* + * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync. + */ + private static Class<?> getBoxingTypeIfPrimitive(Class<?> clazz) { + if (clazz.isPrimitive()) { + if (clazz == Boolean.TYPE) { + return Boolean.class; + } else if (clazz == Character.TYPE) { + return Character.class; + } else if (clazz == Byte.TYPE) { + return Byte.class; + } else if (clazz == Short.TYPE) { + return Short.class; + } else if (clazz == Integer.TYPE) { + return Integer.class; + } else if (clazz == Long.TYPE) { + return Long.class; + } else if (clazz == Float.TYPE) { + return Float.class; + } else { + return Double.class; + } + } else { + return clazz; + } + + } + + /* + * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync. + */ + private static Class<?>[] getComparingParamTypesForVarArgsMethod(Class<?>[] paramTypes, int length) { + Class<?>[] result = new Class<?>[length]; + System.arraycopy(paramTypes, 0, result, 0, paramTypes.length - 1); + Class<?> type = paramTypes[paramTypes.length - 1].getComponentType(); + for (int i = paramTypes.length - 1; i < length; i++) { + result[i] = type; + } + + return result; + } + + /* + * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync. + */ + private static final String paramString(Class<?>[] types) { + if (types != null) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < types.length; i++) { + if (types[i] == null) { + sb.append("null, "); + } else { + sb.append(types[i].getName()).append(", "); + } + } + if (sb.length() > 2) { + sb.setLength(sb.length() - 2); + } + return sb.toString(); + } + return null; + } + + /* + * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync. + */ + static boolean isAssignableFrom(Class<?> src, Class<?> target) { + // src will always be an object + // Short-cut. null is always assignable to an object and in EL null + // can always be coerced to a valid value for a primitive + if (src == null) { + return true; + } + + target = getBoxingTypeIfPrimitive(target); + + return target.isAssignableFrom(src); + } + + /* + * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync. + */ + private static boolean isCoercibleFrom(Object src, Class<?> target) { + // TODO: This isn't pretty but it works. Significant refactoring would + // be required to avoid the exception. + try { + ELSupport.coerceToType(src, target); + } catch (Exception e) { + return false; + } + return true; + } + + /* + * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync. + */ + private static Class<?>[] getTypesFromValues(Object[] values) { + if (values == null) { + return null; + } + + Class<?> result[] = new Class<?>[values.length]; + for (int i = 0; i < values.length; i++) { + if (values[i] == null) { + result[i] = null; + } else { + result[i] = values[i].getClass(); + } + } + return result; + } + + /* + * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync. + * + * Get a public method form a public class or interface of a given method. Note that if a PropertyDescriptor is obtained + * for a non-public class that implements a public interface, the read/write methods will be for the class, and + * therefore inaccessible. To correct this, a version of the same method must be found in a superclass or interface. + * + */ + static Method getMethod(Class<?> type, Method m) { + if (m == null || Modifier.isPublic(type.getModifiers())) { + return m; + } + Class<?>[] inf = type.getInterfaces(); + Method mp = null; + for (int i = 0; i < inf.length; i++) { + try { + mp = inf[i].getMethod(m.getName(), m.getParameterTypes()); + mp = getMethod(mp.getDeclaringClass(), mp); + if (mp != null) { + return mp; + } + } catch (NoSuchMethodException e) { + // Ignore + } + } + Class<?> sup = type.getSuperclass(); + if (sup != null) { + try { + mp = sup.getMethod(m.getName(), m.getParameterTypes()); + mp = getMethod(mp.getDeclaringClass(), mp); + if (mp != null) { + return mp; + } + } catch (NoSuchMethodException e) { + // Ignore + } + } + return null; + } + + /* + * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync. + */ + static Constructor<?> getConstructor(Class<?> type, Constructor<?> c) { + if (c == null || Modifier.isPublic(type.getModifiers())) { + return c; + } + Constructor<?> cp = null; + Class<?> sup = type.getSuperclass(); + if (sup != null) { + try { + cp = sup.getConstructor(c.getParameterTypes()); + cp = getConstructor(cp.getDeclaringClass(), cp); + if (cp != null) { + return cp; + } + } catch (NoSuchMethodException e) { + // Ignore + } + } + return null; + } + + /* + * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync. + */ + static Object[] buildParameters(ELContext context, Class<?>[] parameterTypes, boolean isVarArgs, Object[] params) { + Object[] parameters = null; + if (parameterTypes.length > 0) { + parameters = new Object[parameterTypes.length]; + int paramCount = params == null ? 0 : params.length; + if (isVarArgs) { + int varArgIndex = parameterTypes.length - 1; + // First argCount-1 parameters are standard + for (int i = 0; (i < varArgIndex && i < paramCount); i++) { + parameters[i] = context.convertToType(params[i], parameterTypes[i]); + } + // Last parameter is the varargs + if (parameterTypes.length == paramCount && parameterTypes[varArgIndex] == params[varArgIndex].getClass()) { + parameters[varArgIndex] = params[varArgIndex]; + } else { + Class<?> varArgClass = parameterTypes[varArgIndex].getComponentType(); + final Object varargs = Array.newInstance(varArgClass, (paramCount - varArgIndex)); + for (int i = (varArgIndex); i < paramCount; i++) { + Array.set(varargs, i - varArgIndex, context.convertToType(params[i], varArgClass)); + } + parameters[varArgIndex] = varargs; + } + } else { + for (int i = 0; i < parameterTypes.length && i < paramCount; i++) { + parameters[i] = context.convertToType(params[i], parameterTypes[i]); + } + } + } + return parameters; + } + + /* + * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync. + */ + private abstract static class Wrapper { + + public static List<Wrapper> wrap(Method[] methods, String name) { + List<Wrapper> result = new ArrayList<>(); + for (Method method : methods) { + if (method.getName().equals(name)) { + result.add(new MethodWrapper(method)); + } + } + return result; + } + + public static List<Wrapper> wrap(Constructor<?>[] constructors) { + List<Wrapper> result = new ArrayList<>(); + for (Constructor<?> constructor : constructors) { + result.add(new ConstructorWrapper(constructor)); + } + return result; + } + + public abstract Object unWrap(); + + public abstract Class<?>[] getParameterTypes(); + + public abstract boolean isVarArgs(); + + public abstract boolean isBridge(); + } + + /* + * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync. + */ + private static class MethodWrapper extends Wrapper { + private final Method m; + + public MethodWrapper(Method m) { + this.m = m; + } + + @Override + public Object unWrap() { + return m; + } + + @Override + public Class<?>[] getParameterTypes() { + return m.getParameterTypes(); + } + + @Override + public boolean isVarArgs() { + return m.isVarArgs(); + } + + @Override + public boolean isBridge() { + return m.isBridge(); + } + } + + /* + * This method duplicates code in javax.el.ELUtil. When making changes keep the code in sync. + */ + private static class ConstructorWrapper extends Wrapper { + private final Constructor<?> c; + + public ConstructorWrapper(Constructor<?> c) { + this.c = c; + } + + @Override + public Object unWrap() { + return c; + } + + @Override + public Class<?>[] getParameterTypes() { + return c.getParameterTypes(); + } + + @Override + public boolean isVarArgs() { + return c.isVarArgs(); + } + + @Override + public boolean isBridge() { + return false; + } + } + +}
diff --git a/parent-pom/pom.xml b/parent-pom/pom.xml new file mode 100644 index 0000000..26c74f5 --- /dev/null +++ b/parent-pom/pom.xml
@@ -0,0 +1,150 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + + This program and the accompanying materials are made available under the + terms of the Eclipse Public License v. 2.0, which is available at + http://www.eclipse.org/legal/epl-2.0. + + This Source Code may also be made available under the following Secondary + Licenses when the conditions for such availability set forth in the + Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + version 2 with the GNU Classpath Exception, which is available at + https://www.gnu.org/software/classpath/license.html. + + SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.glassfish.web</groupId> + <artifactId>el</artifactId> + <version>2.2.1-SNAPSHOT</version> + <packaging>pom</packaging> + <name>Expression Language related modules</name> + + <properties> + <findbugs.version>2.3.1</findbugs.version> + <findbugs.exclude /> + <findbugs.threshold>High</findbugs.threshold> + </properties> + + <build> + <plugins> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.5</source> + <target>1.5</target> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-source-plugin</artifactId> + <version> 2.1 </version> + <configuration> + <includePom>true</includePom> + </configuration> + <executions> + <execution> + <id>attach-sources</id> + <goals> + <goal>jar-no-fork</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-javadoc-plugin</artifactId> + <executions> + <execution> + <id>attach-javadocs</id> + <goals> + <goal>jar</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>findbugs-maven-plugin</artifactId> + <version>${findbugs.version}</version> + <configuration> + <threshold>${findbugs.threshold}</threshold> + <excludeFilterFile>${findbugs.exclude}</excludeFilterFile> + <findbugsXmlOutput>true</findbugsXmlOutput> + <findbugsXmlWithMessages>true</findbugsXmlWithMessages> + </configuration> + </plugin> + </plugins> + <extensions> + <extension> + <groupId>org.jvnet.wagon-svn</groupId> + <artifactId>wagon-svn</artifactId> + <version>1.12</version> + </extension> + <extension> + <groupId>org.apache.maven.wagon</groupId> + <artifactId>wagon-webdav</artifactId> + <version>1.0-beta-2</version> + </extension> + </extensions> + </build> + + <reporting> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>findbugs-maven-plugin</artifactId> + <version>2.1</version> + <configuration> + <threshold>High</threshold> + <excludeFilterFile>${findbugs.exclude}</excludeFilterFile> + </configuration> + </plugin> + </plugins> + </reporting> + + <scm> + <connection>scm:svn:svn+ssh://janey@svn.java.net/uel~svn/trunk</connection> + <developerConnection>scm:svn:svn+ssh://janey@svn.java.net/uel~svn/trunk</developerConnection> + <url>svn+ssh://janey@svn.java.net/uel~svn/trunk/</url> + </scm> + + <repositories> + <repository> + <id>glassfish-repository</id> + <url>http://download.java.net/maven/glassfish</url> + </repository> + <repository> + <id>maven2-repository.dev.java.net</id> + <name>Java.net Repository for Maven</name> + <url>http://download.java.net/maven/2</url> + </repository> + </repositories> + <pluginRepositories> + <pluginRepository> + <id>glassfish-repository</id> + <name>Java.net Repository for Maven 2</name> + <url>http://download.java.net/maven/glassfish</url> + <snapshots> + <updatePolicy>never</updatePolicy> + </snapshots> + </pluginRepository> + </pluginRepositories> + <distributionManagement> + <site> + <id>java.net-el</id> + <url>java-net:/uel~svn/trunk/repo/</url> + </site> + <repository> + <uniqueVersion>false</uniqueVersion> + <id>java.net-maven2-repository</id> + <url>java-net:/maven2-repository~svn/trunk/repository/</url> + </repository> + </distributionManagement> +</project> +
diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..1ae64c0 --- /dev/null +++ b/pom.xml
@@ -0,0 +1,328 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Copyright (c) 1997, 2018 Oracle and/or its affiliates and others. + All rights reserved. + + This program and the accompanying materials are made available under the + terms of the Eclipse Public License v. 2.0, which is available at + http://www.eclipse.org/legal/epl-2.0. + + This Source Code may also be made available under the following Secondary + Licenses when the conditions for such availability set forth in the + Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + version 2 with the GNU Classpath Exception, which is available at + https://www.gnu.org/software/classpath/license.html. + + SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.eclipse.ee4j</groupId> + <artifactId>project</artifactId> + <version>1.0.7</version> + </parent> + + <groupId>org.glassfish</groupId> + <artifactId>jakarta.el</artifactId> + <version>3.0.4</version> + + <name>Jakarta Expression Language 3.0</name> + <description> + Jakarta Expression Language provides a specification document, API, reference implementation and TCK + that describes an expression language for Java applications. + </description> + <url>https://projects.eclipse.org/projects/ee4j.el</url> + + <properties> + <!-- the bundle build number must be the same as the maven number --> + <bundle.version>${project.version}</bundle.version> + <!-- The most current api version --> + <spec.version>3.0</spec.version> + <extensionName>javax.el</extensionName> + <bundle.symbolicName>com.sun.el.javax.el</bundle.symbolicName> + <vendorName>Oracle Corporation</vendorName> + <findbugs.version>2.5.2</findbugs.version> + <findbugs.exclude>${project.basedir}/exclude.xml</findbugs.exclude> + <findbugs.threshold>High</findbugs.threshold> + </properties> + + <developers> + <developer> + <id>yaminikb</id> + <name>Yamini K B</name> + <organization>Oracle Corporation</organization> + <organizationUrl>http://www.oracle.com/</organizationUrl> + </developer> + </developers> + + <contributors> + <contributor> + <name>Kin-man Chung</name> + </contributor> + </contributors> + + <issueManagement> + <system>github</system> + <url>https://github.com/eclipse-ee4j/el-ri/issues</url> + </issueManagement> + + <licenses> + <license> + <name>EPL 2.0</name> + <url>http://www.eclipse.org/legal/epl-2.0</url> + <distribution>repo</distribution> + </license> + <license> + <name>GPL2 w/ CPE</name> + <url>https://www.gnu.org/software/classpath/license.html</url> + <distribution>repo</distribution> + </license> + </licenses> + + <mailingLists> + <mailingList> + <name>Jakarta Expression Language mailing list</name> + <post>el-dev@eclipse.org</post> + <subscribe>https://dev.eclipse.org/mailman/listinfo/el-dev</subscribe> + <unsubscribe>https://dev.eclipse.org/mailman/listinfo/el-dev</unsubscribe> + <archive>https://dev.eclipse.org/mhonarc/lists/el-dev</archive> + </mailingList> + </mailingLists> + + <scm> + <connection>scm:git:https://github.com/eclipse-ee4j/el-ri.git</connection> + <developerConnection> + scm:git:git@github.com:eclipse-ee4j/el-ri.git + </developerConnection> + <url>https://github.com/eclipse-ee4j/el-ri</url> + <tag>HEAD</tag> + </scm> + + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.12</version> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <resources> + <resource> + <directory>api/src/main/java</directory> + <includes> + <include>**/*.properties</include> + <include>**/*.xml</include> + </includes> + </resource> + <resource> + <directory>impl/src/main/java</directory> + <includes> + <include>**/*.properties</include> + <include>**/*.xml</include> + </includes> + </resource> + <resource> + <directory>${project.basedir}</directory> + <includes> + <include>LICENSE.md</include> + <include>NOTICE.md</include> + </includes> + <targetPath>META-INF</targetPath> + </resource> + </resources> + + <plugins> + <!-- Use this to include both the api and impl sources --> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <version>1.1</version> + <executions> + <execution> + <id>add-source</id> + <phase>generate-sources</phase> + <goals> + <goal>add-source</goal> + </goals> + <configuration> + <sources> + <source>api/src/main/java</source> + <source>impl/src/main/java</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + + <!-- + Configure maven-bundle-plugin to generate OSGi manifest. + + Please note: we use the manifest goal only and not the bundle goal. + The bundle goal can lead to very surprising results if the package names are not correctly specified. + So, we use the jar plugin to generate the jar. + --> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <version>1.4.3</version> + <configuration> + <instructions> + <Bundle-SymbolicName>${bundle.symbolicName}</Bundle-SymbolicName> + <Bundle-Description> + Expression Language ${spec.version} API and Implementation + + </Bundle-Description> + <Bundle-Version>${bundle.version}</Bundle-Version> + <Extension-Name>${extensionName}</Extension-Name> + <Specification-Version>${spec.version}</Specification-Version> + <Specification-Vendor>${vendorName}</Specification-Vendor> + <Implementation-Version>${project.version}</Implementation-Version> + <Implementation-Vendor>${vendorName}</Implementation-Vendor> + </instructions> + </configuration> + <executions> + <execution> + <id>bundle-manifest</id> + <phase>process-classes</phase> + <goals> + <goal>manifest</goal> + </goals> + </execution> + </executions> + </plugin> + + <!-- Adds the manifest file created by the org.apache.felix:maven-bundle-plugin --> + <plugin> + <artifactId>maven-jar-plugin</artifactId> + <version>2.4</version> + <configuration> + <archive> + <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile> + </archive> + </configuration> + </plugin> + + <!-- Restricts the Java version to 1.7 --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.0</version> + <configuration> + <source>1.7</source> + <target>1.7</target> + <compilerArgument>-Xlint:unchecked</compilerArgument> + </configuration> + </plugin> + + <!-- Creates the source jar --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-source-plugin</artifactId> + <version> 2.2.1 </version> + <configuration> + <includePom>true</includePom> + </configuration> + <executions> + <execution> + <id>attach-sources</id> + <goals> + <goal>jar-no-fork</goal> + </goals> + </execution> + </executions> + </plugin> + + <!-- Creates the javadoc jar --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-javadoc-plugin</artifactId> + <version>3.0.1</version> + <executions> + <execution> + <id>attach-javadocs</id> + <goals> + <goal>jar</goal> + </goals> + <configuration> + <source>1.7</source> + <sourcepath>api/src;impl/src</sourcepath> + <additionalJOption>-Xdoclint:none</additionalJOption> + <links> + <link>https://javaee.github.io/javaee-spec/javadocs/</link> + </links> + <groups> + <group> + <title>Jakarta Expression Language 3.0 API and Implementation</title> + <packages>com.sun.el</packages> + </group> + </groups> + <bottom> Copyright (c) 2013 Oracle and/or its affiliates. All Rights Reserved. Use is subject to license terms. </bottom> + </configuration> + </execution> + </executions> + </plugin> + + <!-- Use ant to manually invoke javacc, as this required is very infrequently <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>javacc-maven-plugin</artifactId> + <version>2.6</version> <executions> <execution> <id>jjtree-javacc</id> <goals> <goal>jjtree-javacc</goal> </goals> <configuration> <sourceDirectory>src/main/java/com/sun/el/parser</sourceDirectory> + <outputDirectory>src/main/java/com/sun/el/parser</outputDirectory> </configuration> </execution> </executions> </plugin> --> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>findbugs-maven-plugin</artifactId> + <version>${findbugs.version}</version> + <configuration> + <threshold>${findbugs.threshold}</threshold> + <excludeFilterFile>${findbugs.exclude}</excludeFilterFile> + <findbugsXmlOutput>true</findbugsXmlOutput> + <findbugsXmlWithMessages>true</findbugsXmlWithMessages> + </configuration> + </plugin> + + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <version>2.7.1</version> + <dependencies> + <dependency> + <groupId>org.apache.maven.surefire</groupId> + <artifactId>surefire-junit47</artifactId> + <version>2.7.1</version> + </dependency> + </dependencies> + <configuration> + <forkMode>never</forkMode> + </configuration> + </plugin> + </plugins> + </build> + + <reporting> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>findbugs-maven-plugin</artifactId> + <version>${findbugs.version}</version> + <configuration> + <threshold>${findbugs.threshold}</threshold> + <excludeFilterFile>${findbugs.exclude}</excludeFilterFile> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-javadoc-plugin</artifactId> + <version>3.0.1</version> + <configuration> + <excludePackageNames>com.sun.el.parser</excludePackageNames> + <sourceFileExcludes> + <sourceFileExclude>**/parser/*.java</sourceFileExclude> + </sourceFileExcludes> + </configuration> + </plugin> + </plugins> + </reporting> + +</project>
diff --git a/spec/README.md b/spec/README.md new file mode 100644 index 0000000..70f25b2 --- /dev/null +++ b/spec/README.md
@@ -0,0 +1,22 @@ +Jakarta Expression Language Specification +============================ + +This project generates the Jakarta Expression Language Specification. + +Building +-------- + +Prerequisites: + +* JDK8+ +* Maven 3.0.3+ + +Run the full build: + +`mvn install` + +Locate the html files: +- `target/generated-docs/expression-language-spec-<version>.html` + +Locate the PDF files: +- `target/generated-docs/expression-language-spec-<version>.pdf`
diff --git a/spec/assembly.xml b/spec/assembly.xml new file mode 100644 index 0000000..7f30954 --- /dev/null +++ b/spec/assembly.xml
@@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- + + Copyright (c) 2019 Contributors to the Eclipse Foundation. + + This program and the accompanying materials are made available under the + terms of the Eclipse Public License v. 2.0, which is available at + http://www.eclipse.org/legal/epl-2.0. + + This Source Code may also be made available under the following Secondary + Licenses when the conditions for such availability set forth in the + Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + version 2 with the GNU Classpath Exception, which is available at + https://www.gnu.org/software/classpath/license.html. + + SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +--> + +<assembly> + <id>spec</id> + <formats> + <format>zip</format> + </formats> + <baseDirectory>expression-language-spec</baseDirectory> + <fileSets> + <fileSet> + <directory>target/generated-docs</directory> + <outputDirectory></outputDirectory> + </fileSet> + </fileSets> +</assembly>
diff --git a/spec/pom.xml b/spec/pom.xml new file mode 100644 index 0000000..8eacf9b --- /dev/null +++ b/spec/pom.xml
@@ -0,0 +1,177 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Copyright (c) 2019 Contributors to the Eclipse Foundation. + + This program and the accompanying materials are made available under the + terms of the Eclipse Public License v. 2.0, which is available at + http://www.eclipse.org/legal/epl-2.0. + + This Source Code may also be made available under the following Secondary + Licenses when the conditions for such availability set forth in the + Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + version 2 with the GNU Classpath Exception, which is available at + https://www.gnu.org/software/classpath/license.html. + + SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.eclipse.ee4j</groupId> + <artifactId>project</artifactId> + <version>1.0.5</version> + <relativePath/> + </parent> + + <groupId>org.eclipse.ee4j.expression-language</groupId> + <artifactId>expression-language-spec</artifactId> + <version>3.0</version> + <packaging>pom</packaging> + <name>Jakarta Expression Language Specification</name> + + <properties> + <site.output.dir>${project.build.directory}/staging</site.output.dir> + <maven.site.skip>true</maven.site.skip> + <asciidoctor.maven.plugin.version>1.5.7.1</asciidoctor.maven.plugin.version> + <asciidoctorj.version>1.6.2</asciidoctorj.version> + <asciidoctorj.pdf.version>1.5.0-alpha.16</asciidoctorj.pdf.version> + <jruby.version>9.2.6.0</jruby.version> + <!-- status: DRAFT, BETA, etc., or blank for final --> + <status>DRAFT</status> + <maven.build.timestamp.format>MMMM dd, yyyy</maven.build.timestamp.format> + <revisiondate>${maven.build.timestamp}</revisiondate> + </properties> + + <build> + <defaultGoal>package</defaultGoal> + <plugins> + <plugin> + <groupId>org.asciidoctor</groupId> + <artifactId>asciidoctor-maven-plugin</artifactId> + <version>${asciidoctor.maven.plugin.version}</version> + <dependencies> + <dependency> + <groupId>org.jruby</groupId> + <artifactId>jruby-complete</artifactId> + <version>${jruby.version}</version> + </dependency> + <dependency> + <groupId>org.asciidoctor</groupId> + <artifactId>asciidoctorj</artifactId> + <version>${asciidoctorj.version}</version> + </dependency> + <dependency> + <groupId>org.asciidoctor</groupId> + <artifactId>asciidoctorj-pdf</artifactId> + <version>${asciidoctorj.pdf.version}</version> + </dependency> + </dependencies> + <executions> + <execution> + <id>asciidoc-to-html</id> + <phase>generate-resources</phase> + <goals> + <goal>process-asciidoc</goal> + </goals> + <configuration> + <backend>html5</backend> + <outputFile>${project.build.directory}/generated-docs/expression-language-spec-${project.version}.html</outputFile> + <attributes> + <doctype>book</doctype> + <status>${status}</status> + <data-uri /> + <icons>font</icons> + <toc>left</toc> + <icons>font</icons> + <sectanchors>true</sectanchors> + <idprefix /> + <idseparator>-</idseparator> + <docinfo1>true</docinfo1> + </attributes> + </configuration> + </execution> + <execution> + <id>asciidoc-to-pdf</id> + <phase>generate-resources</phase> + <goals> + <goal>process-asciidoc</goal> + </goals> + <configuration> + <backend>pdf</backend> + <outputFile>${project.build.directory}/generated-docs/expression-language-spec-${project.version}.pdf</outputFile> + <attributes> + <pdf-stylesdir>${project.basedir}/src/main/theme</pdf-stylesdir> + <pdf-style>jakartaee</pdf-style> + <doctype>book</doctype> + <status>${status}</status> + <data-uri /> + <icons>font</icons> + <pagenums /> + <toc /> + <icons>font</icons> + <sectanchors>true</sectanchors> + <idprefix /> + <idseparator>-</idseparator> + <docinfo1>true</docinfo1> + <embedAssets>true</embedAssets> + </attributes> + </configuration> + </execution> + </executions> + <configuration> + <sourceDocumentName>expression-language-spec.adoc</sourceDocumentName> + <sourceHighlighter>coderay</sourceHighlighter> + <attributes> + <revnumber>${project.version}</revnumber> + <revremark>${status}</revremark> + <revdate>${revisiondate}</revdate> + </attributes> + </configuration> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-release-plugin</artifactId> + <version>2.5.2</version> + <configuration> + <mavenExecutorId>forked-path</mavenExecutorId> + <useReleaseProfile>false</useReleaseProfile> + <arguments>${release.arguments}</arguments> + </configuration> + <dependencies> + <dependency> + <groupId>org.apache.maven.scm</groupId> + <artifactId>maven-scm-provider-gitexe</artifactId> + <version>1.9.4</version> + </dependency> + </dependencies> + </plugin> + + <!-- + This is the rule that builds the zip file for download. + --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-assembly-plugin</artifactId> + <version>3.1.1</version> + <inherited>false</inherited> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>single</goal> + </goals> + <configuration> + <appendAssemblyId>false</appendAssemblyId> + <descriptors> + <descriptor>assembly.xml</descriptor> + </descriptors> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project>
diff --git a/spec/src/main/asciidoc/expression-language-spec.adoc b/spec/src/main/asciidoc/expression-language-spec.adoc new file mode 100644 index 0000000..edf075d --- /dev/null +++ b/spec/src/main/asciidoc/expression-language-spec.adoc
@@ -0,0 +1,28 @@ +// +// Copyright (c) 2017, 2019 Contributors to the Eclipse Foundation +// + += Jakarta Expression Language +:authors: Jakarta Expression Language Team, https://projects.eclipse.org/projects/ee4j.el +:email: https://dev.eclipse.org/mailman/listinfo/el-dev +:version-label!: +:doctype: book +:license: Eclipse Foundation Specification License v1.0 +:source-highlighter: coderay +:toc: left +:toclevels: 4 +:sectnumlevels: 4 +:sectanchors: +ifdef::backend-pdf[] +:pagenums: +:numbered: +:title-logo-image: image:jakarta_ee_logo_schooner_color_stacked_default.png[pdfwidth=4.25in,align=right] +endif::[] + +// == License +:sectnums!: +include::license-efsl.adoc[] + +// == Scope +:sectnums: +include::scope.adoc[]
diff --git a/spec/src/main/asciidoc/images/jakarta_ee_logo_schooner_color_stacked_default.png b/spec/src/main/asciidoc/images/jakarta_ee_logo_schooner_color_stacked_default.png new file mode 100644 index 0000000..97b46ce --- /dev/null +++ b/spec/src/main/asciidoc/images/jakarta_ee_logo_schooner_color_stacked_default.png Binary files differ
diff --git a/spec/src/main/asciidoc/license-efsl.adoc b/spec/src/main/asciidoc/license-efsl.adoc new file mode 100644 index 0000000..d92d677 --- /dev/null +++ b/spec/src/main/asciidoc/license-efsl.adoc
@@ -0,0 +1,74 @@ +[subs="normal"] +.... +Specification: {doctitle} + +Version: {revnumber} + +Status: {revremark} + +Release: {revdate} +.... + +Copyright (c) 2019 Eclipse Foundation. + +=== Eclipse Foundation Specification License + +By using and/or copying this document, or the Eclipse Foundation +document from which this statement is linked, you (the licensee) agree +that you have read, understood, and will comply with the following +terms and conditions: + +Permission to copy, and distribute the contents of this document, or +the Eclipse Foundation document from which this statement is linked, in +any medium for any purpose and without fee or royalty is hereby +granted, provided that you include the following on ALL copies of the +document, or portions thereof, that you use: + +* link or URL to the original Eclipse Foundation document. +* All existing copyright notices, or if one does not exist, a notice + (hypertext is preferred, but a textual representation is permitted) + of the form: "Copyright (c) [$date-of-document] + Eclipse Foundation, Inc. <<url to this license>>" + +Inclusion of the full text of this NOTICE must be provided. We +request that authorship attribution be provided in any software, +documents, or other items or products that you create pursuant to the +implementation of the contents of this document, or any portion +thereof. + +No right to create modifications or derivatives of Eclipse Foundation +documents is granted pursuant to this license, except anyone may +prepare and distribute derivative works and portions of this document +in software that implements the specification, in supporting materials +accompanying such software, and in documentation of such software, +PROVIDED that all such works include the notice below. HOWEVER, the +publication of derivative works of this document for use as a technical +specification is expressly prohibited. + +The notice is: + +"Copyright (c) 2018 Eclipse Foundation. This software or +document includes material copied from or derived from [title and URI +of the Eclipse Foundation specification document]." + +==== Disclaimers + +THIS DOCUMENT IS PROVIDED "AS IS," AND THE COPYRIGHT +HOLDERS AND THE ECLIPSE FOUNDATION MAKE NO REPRESENTATIONS OR +WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, +NON-INFRINGEMENT, OR TITLE; THAT THE CONTENTS OF THE DOCUMENT ARE +SUITABLE FOR ANY PURPOSE; NOR THAT THE IMPLEMENTATION OF SUCH CONTENTS +WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR +OTHER RIGHTS. + +THE COPYRIGHT HOLDERS AND THE ECLIPSE FOUNDATION WILL NOT BE LIABLE +FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT +OF ANY USE OF THE DOCUMENT OR THE PERFORMANCE OR IMPLEMENTATION OF THE +CONTENTS THEREOF. + +The name and trademarks of the copyright holders or the Eclipse +Foundation may NOT be used in advertising or publicity pertaining to +this document or its contents without specific, written prior +permission. Title to copyright in this document will at all times +remain with copyright holders.
diff --git a/spec/src/main/asciidoc/scope.adoc b/spec/src/main/asciidoc/scope.adoc new file mode 100644 index 0000000..778b8b6 --- /dev/null +++ b/spec/src/main/asciidoc/scope.adoc
@@ -0,0 +1,3 @@ +== Specification Scope + +Jakarta Expression Language defines an expression language for Java applications.
diff --git a/spec/src/main/theme/jakartaee-theme.yml b/spec/src/main/theme/jakartaee-theme.yml new file mode 100644 index 0000000..6092a2f --- /dev/null +++ b/spec/src/main/theme/jakartaee-theme.yml
@@ -0,0 +1,299 @@ +# +# Following is the asciidoctor-pdf default theme [1], with small +# customizations, mostly for header and footer, marked "EE". +# +# [1] https://github.com/asciidoctor/asciidoctor-pdf/blob/master/data/themes/default-theme.yml +# +font: + catalog: + # Noto Serif supports Latin, Latin-1 Supplement, Latin Extended-A, Greek, Cyrillic, Vietnamese & an assortment of symbols + Noto Serif: + normal: notoserif-regular-subset.ttf + bold: notoserif-bold-subset.ttf + italic: notoserif-italic-subset.ttf + bold_italic: notoserif-bold_italic-subset.ttf + # M+ 1mn supports ASCII and the circled numbers used for conums + M+ 1mn: + normal: mplus1mn-regular-ascii-conums.ttf + bold: mplus1mn-bold-ascii.ttf + italic: mplus1mn-italic-ascii.ttf + bold_italic: mplus1mn-bold_italic-ascii.ttf + # M+ 1p supports Latin, Latin-1 Supplement, Latin Extended, Greek, Cyrillic, Vietnamese, Japanese & an assortment of symbols + # It also provides arrows for ->, <-, => and <= replacements in case these glyphs are missing from font + M+ 1p Fallback: + normal: mplus1p-regular-fallback.ttf + bold: mplus1p-regular-fallback.ttf + italic: mplus1p-regular-fallback.ttf + bold_italic: mplus1p-regular-fallback.ttf + fallbacks: + - M+ 1p Fallback +page: + background_color: ffffff + layout: portrait + margin: [0.5in, 0.67in, 0.67in, 0.67in] + # margin_inner and margin_outer keys are used for recto/verso print margins when media=prepress + margin_inner: 0.75in + margin_outer: 0.59in + #size: A4 # EE + size: Letter # EE +base: + align: justify + # color as hex string (leading # is optional) + font_color: 333333 + # color as RGB array + #font_color: [51, 51, 51] + # color as CMYK array (approximated) + #font_color: [0, 0, 0, 0.92] + #font_color: [0, 0, 0, 92%] + font_family: Noto Serif + # choose one of these font_size/line_height_length combinations + #font_size: 14 + #line_height_length: 20 + #font_size: 11.25 + #line_height_length: 18 + #font_size: 11.2 + #line_height_length: 16 + font_size: 10.5 + #line_height_length: 15 + # correct line height for Noto Serif metrics + line_height_length: 12 + #font_size: 11.25 + #line_height_length: 18 + line_height: $base_line_height_length / $base_font_size + font_size_large: round($base_font_size * 1.25) + font_size_small: round($base_font_size * 0.85) + font_size_min: $base_font_size * 0.75 + font_style: normal + border_color: eeeeee + border_radius: 4 + border_width: 0.5 +# FIXME vertical_rhythm is weird; we should think in terms of ems +#vertical_rhythm: $base_line_height_length * 2 / 3 +# correct line height for Noto Serif metrics (comes with built-in line height) +vertical_rhythm: $base_line_height_length +horizontal_rhythm: $base_line_height_length +# QUESTION should vertical_spacing be block_spacing instead? +vertical_spacing: $vertical_rhythm +link: + font_color: 428bca +# literal is currently used for inline monospaced in prose and table cells +literal: + font_color: b12146 + font_family: M+ 1mn +menu_caret_content: " <font size=\"1.15em\"><color rgb=\"b12146\">\u203a</color></font> " +heading: + align: left + #font_color: 181818 + font_color: $base_font_color + font_family: $base_font_family + font_style: bold + # h1 is used for part titles (book doctype) or the doctitle (article doctype) + #h1_font_size: floor($base_font_size * 2.6) # EE + h1_font_size: floor($base_font_size * 2.5) # EE, squeeze title onto one line + # h2 is used for chapter titles (book doctype only) + h2_font_size: floor($base_font_size * 2.15) + h3_font_size: round($base_font_size * 1.7) + h4_font_size: $base_font_size_large + h5_font_size: $base_font_size + h6_font_size: $base_font_size_small + #line_height: 1.4 + # correct line height for Noto Serif metrics (comes with built-in line height) + line_height: 1 + margin_top: $vertical_rhythm * 0.4 + margin_bottom: $vertical_rhythm * 0.9 +title_page: + align: right + logo: + top: 10% + title: + top: 55% + font_size: $heading_h1_font_size + font_color: 999999 + line_height: 0.9 + subtitle: + font_size: $heading_h3_font_size + font_style: bold_italic + line_height: 1 + authors: + margin_top: $base_font_size * 1.25 + font_size: $base_font_size_large + font_color: 181818 + revision: + margin_top: $base_font_size * 1.25 +block: + margin_top: 0 + margin_bottom: $vertical_rhythm +caption: + align: left + font_size: $base_font_size * 0.95 + font_style: italic + # FIXME perhaps set line_height instead of / in addition to margins? + margin_inside: $vertical_rhythm / 3 + #margin_inside: $vertical_rhythm / 4 + margin_outside: 0 +lead: + font_size: $base_font_size_large + line_height: 1.4 +abstract: + font_color: 5c6266 + font_size: $lead_font_size + line_height: $lead_line_height + font_style: italic + first_line_font_style: bold + title: + align: center + font_color: $heading_font_color + font_family: $heading_font_family + font_size: $heading_h4_font_size + font_style: $heading_font_style +admonition: + column_rule_color: $base_border_color + column_rule_width: $base_border_width + padding: [0, $horizontal_rhythm, 0, $horizontal_rhythm] + #icon: + # tip: + # name: fa-lightbulb-o + # stroke_color: 111111 + # size: 24 + label: + text_transform: uppercase + font_style: bold +blockquote: + font_color: $base_font_color + font_size: $base_font_size_large + border_color: $base_border_color + border_width: 5 + # FIXME disable negative padding bottom once margin collapsing is implemented + padding: [0, $horizontal_rhythm, $block_margin_bottom * -0.75, $horizontal_rhythm + $blockquote_border_width / 2] + cite_font_size: $base_font_size_small + cite_font_color: 999999 +# code is used for source blocks (perhaps change to source or listing?) +code: + font_color: $base_font_color + font_family: $literal_font_family + font_size: ceil($base_font_size) + padding: $code_font_size + line_height: 1.25 + # line_gap is an experimental property to control how a background color is applied to an inline block element + line_gap: 3.8 + background_color: f5f5f5 + border_color: cccccc + border_radius: $base_border_radius + border_width: 0.75 +conum: + font_family: M+ 1mn + font_color: $literal_font_color + font_size: $base_font_size + line_height: 4 / 3 +example: + border_color: $base_border_color + border_radius: $base_border_radius + border_width: 0.75 + background_color: ffffff + # FIXME reenable padding bottom once margin collapsing is implemented + padding: [$vertical_rhythm, $horizontal_rhythm, 0, $horizontal_rhythm] +image: + align: left +prose: + margin_top: $block_margin_top + margin_bottom: $block_margin_bottom +sidebar: + background_color: eeeeee + border_color: e1e1e1 + border_radius: $base_border_radius + border_width: $base_border_width + # FIXME reenable padding bottom once margin collapsing is implemented + padding: [$vertical_rhythm, $vertical_rhythm * 1.25, 0, $vertical_rhythm * 1.25] + title: + align: center + font_color: $heading_font_color + font_family: $heading_font_family + font_size: $heading_h4_font_size + font_style: $heading_font_style +thematic_break: + border_color: $base_border_color + border_style: solid + border_width: $base_border_width + margin_top: $vertical_rhythm * 0.5 + margin_bottom: $vertical_rhythm * 1.5 +description_list: + term_font_style: bold + term_spacing: $vertical_rhythm / 4 + description_indent: $horizontal_rhythm * 1.25 +outline_list: + indent: $horizontal_rhythm * 1.5 + #marker_font_color: 404040 + # NOTE outline_list_item_spacing applies to list items that do not have complex content + item_spacing: $vertical_rhythm / 2 +table: + background_color: $page_background_color + #head_background_color: <hex value> + #head_font_color: $base_font_color + head_font_style: bold + #body_background_color: <hex value> + body_stripe_background_color: f9f9f9 + foot_background_color: f0f0f0 + border_color: dddddd + border_width: $base_border_width + cell_padding: 3 +toc: + indent: $horizontal_rhythm + line_height: 1.4 + dot_leader: + #content: ". " + font_color: a9a9a9 + #levels: 2 3 +# NOTE in addition to footer, header is also supported +footer: + font_size: $base_font_size_small + # NOTE if background_color is set, background and border will span width of page + #border_color: dddddd # EE + #border_width: 0.25 # EE + height: $base_line_height_length * 2.5 + line_height: 1 + padding: [$base_line_height_length / 2, 1, 0, 1] + vertical_align: top + #image_vertical_align: <alignment> or <number> + # additional attributes for content: + # * {page-count} + # * {page-number} + # * {document-title} + # * {document-subtitle} + # * {chapter-title} + # * {section-title} + # * {section-or-chapter-title} + recto: + #columns: "<50% =0% >50%" + right: + #content: '{page-number}' # EE + #content: '{section-or-chapter-title} | {page-number}' + #content: '{document-title} | {page-number}' + content: '{document-title}{nbsp}{nbsp}{nbsp} *{page-number}*' # EE + #center: + # content: '{page-number}' + left: # EE + content: '{status}' # EE + verso: + #columns: $footer_recto_columns + left: + #content: $footer_recto_right_content # EE + #content: '{page-number} | {chapter-title}' + content: '*{page-number}* {nbsp}{nbsp}{nbsp}{document-title}' # EE + #center: + # content: '{page-number}' + right: # EE + content: '{status}' # EE +header: # EE + font_size: $base_font_size_small # EE + border_color: dddddd # EE + border_width: 0.25 # EE + height: $base_line_height_length * 2.5 # EE + line_height: 1 # EE + padding: [$base_line_height_length / 2, 1, 0, 1] # EE + vertical_align: top # EE + recto: # EE + right: # EE + content: '{section-or-chapter-title}' # EE + verso: # EE + left: # EE + content: '{section-or-chapter-title}' # EE
diff --git a/src/assembly/assembly.xml b/src/assembly/assembly.xml new file mode 100644 index 0000000..8780af6 --- /dev/null +++ b/src/assembly/assembly.xml
@@ -0,0 +1,45 @@ +<!-- + + Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + + This program and the accompanying materials are made available under the + terms of the Eclipse Public License v. 2.0, which is available at + http://www.eclipse.org/legal/epl-2.0. + + This Source Code may also be made available under the following Secondary + Licenses when the conditions for such availability set forth in the + Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + version 2 with the GNU Classpath Exception, which is available at + https://www.gnu.org/software/classpath/license.html. + + SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +--> + +<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd"> + + <id>el-sources</id> + <formats> + <format>zip</format> + </formats> + + <fileSets> + <fileSet> + <directory>${project.basedir}</directory> + <outputDirectory>/</outputDirectory> + <excludes> + <exclude>spec/**</exclude> + <exclude>fonts/**</exclude> + <exclude>target/**</exclude> + <exclude>api/target/**</exclude> + <exclude>src/assembly/**</exclude> + </excludes> + </fileSet> + <fileSet> + <outputDirectory>/</outputDirectory> + <directory>${project.build.directory}/license</directory> + </fileSet> + </fileSets> +</assembly>
diff --git a/src/test/java/org/glassfish/el/test/ConvertTest.java b/src/test/java/org/glassfish/el/test/ConvertTest.java new file mode 100644 index 0000000..814e924 --- /dev/null +++ b/src/test/java/org/glassfish/el/test/ConvertTest.java
@@ -0,0 +1,115 @@ +/* + * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.el.test; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; + +import javax.el.*; + +/** + * + * @author kichung + */ +public class ConvertTest { + ELProcessor elp; + + public ConvertTest() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + @Before + public void setUp() { + elp = new ELProcessor(); + } + + @After + public void tearDown() { + } + + static public class MyBean { + String name; + int pint; + Integer integer; + + MyBean() { + + } + MyBean(String name) { + this.name = name; + } + public String getName() { + return this.name; + } + public void setPint(int v) { + this.pint = v; + } + public int getPint() { + return this.pint; + } + + public void setInteger(Integer i){ + this.integer = i; + } + + public Integer getInteger() { + return this.integer; + } + } + @Test + public void testVoid() { + MyBean bean = new MyBean(); + elp.defineBean("bean", bean); + // Assig null to int is 0; + Object obj = elp.eval("bean.pint = null"); + assertEquals(obj, null); + assertEquals(bean.getPint(), 0); + + // Assig null to Integer is null + elp.setValue("bean.integer", null); + assertEquals(bean.getInteger(), null); + } + + @Test + public void testCustom() { + elp.getELManager().addELResolver(new TypeConverter() { + @Override + public Object convertToType(ELContext context, Object obj, Class<?> type) { + if (obj instanceof String && type == MyBean.class) { + context.setPropertyResolved(true); + return new MyBean((String) obj); + } + return null; + } + }); + + Object val = elp.getValue("'John Doe'", MyBean.class); + assertTrue(val instanceof MyBean); + assertEquals(((MyBean)val).getName(), "John Doe"); + } +}
diff --git a/src/test/java/org/glassfish/el/test/Customer.java b/src/test/java/org/glassfish/el/test/Customer.java new file mode 100644 index 0000000..533ec01 --- /dev/null +++ b/src/test/java/org/glassfish/el/test/Customer.java
@@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.el.test; + +import java.util.List; +import java.util.ArrayList; + +public class Customer { + int customerID; + String name; + String address; + String city; + String country; + String phone; + List<Order> orders; + + public Customer(int customerID, String name, String address, String city, + String country, String phone) { + this.customerID = customerID; + this.name = name; + this.address = address; + this.city = city; + this.country = country; + this.phone = phone; + this.orders = new ArrayList<Order>(); + } + + public String toString() { + return "Customer: " + customerID + ", " + name + ", " + city + ", " + + country; + } + + public int getCustomerID() { return customerID;} + public String getName() { return name;} + public String getAddress() { return address; } + public String getCity() { return city; } + public String getCountry() { return country; } + public String getPhone() { return phone; } + public List<Order> getOrders() { return orders; } +}
diff --git a/src/test/java/org/glassfish/el/test/DataBase.java b/src/test/java/org/glassfish/el/test/DataBase.java new file mode 100644 index 0000000..51daab7 --- /dev/null +++ b/src/test/java/org/glassfish/el/test/DataBase.java
@@ -0,0 +1,102 @@ +/* + * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.el.test; + +import java.util.List; +import java.util.ArrayList; + +public class DataBase { + + private int curCustomer = 100; + private int curProduct = 200; + private int curOrder = 10; + private boolean inited; + + private List<Customer> customers; + private List<Product> products; + private List<Order> orders; + + public List<Customer> getCustomers() { return this.customers; } + public List<Product> getProducts() { return this.products; } + public List<Order> getOrders() { return this.orders; } + + public void init() { + if (inited) { + return; + } + + inited = true; + customers = new ArrayList<Customer>(); + orders = new ArrayList<Order>(); + products = new ArrayList<Product>(); + initCustomer(); + initProduct(); + initOrder(); + } + + void initCustomer() { + c("John Doe", "123 Willow Road", "Menlo Park", "USA", + "650-734-2187"); + c("Mary Lane", "75 State Street", "Atlanta", "USA", "302-145-8765"); + c("Charlie Yeh", "5 Nathan Road", "Kowlon", "Hong Kong", "11-7565-2323"); + } + + void initProduct() { + p("Eagle", "book", 12.50, 100); // id: 200 + p("Coming Home", "dvd", 8.00, 50); // id: 201 + p("Greatest Hits", "cd", 6.5, 200); // id: 202 + p("History of Golf", "book", 11.0, 30); // id: 203 + p("Toy Story", "dvd", 10.00, 1000); // id: 204 + p("iSee", "book", 12.50, 150); // 205 + } + + void initOrder() { + o(100, new Date(2010, 2, 18), 20.80); + o(100, new Date(2011, 5, 3), 34.50); + o(100, new Date(2011, 8, 2), 210.75); + o(101, new Date(2011, 1, 15), 50.23); + o(101, new Date(2012, 1, 3), 126.77); + o(102, new Date(2011, 4, 15), 101.20); + } + + void c(String name, String address, String city, + String country, String phone) { + customers.add(new Customer(curCustomer++, name, address, + city, country, phone)); + } + + void o(int customerID, Date orderDate, double total) { + Order order = new Order(curOrder++, customerID, orderDate, total); + this.orders.add(order); + findCustomer(customerID).getOrders().add(order); + } + + void p(String name, String category, double unitPrice, int unitsInStock) { + products.add(new Product(curProduct++, name, category, unitPrice, + unitsInStock)); + } + + private Customer findCustomer(int id) { + for(Customer customer: customers) { + if (customer.customerID == id) { + return customer; + } + } + return null; + } +} +
diff --git a/src/test/java/org/glassfish/el/test/Date.java b/src/test/java/org/glassfish/el/test/Date.java new file mode 100644 index 0000000..f202fb2 --- /dev/null +++ b/src/test/java/org/glassfish/el/test/Date.java
@@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.el.test; + +public class Date { + int year, month, date; + + public Date(int year, int month, int date) { + this.year = year; + this.month = month; + this.date = date; + } + + public int getYear() { return year; } + public int getMonth() { return month; } + public int getDate() { return date; } + + public String toString() { + return "" + month + "/" + date + "/" + year; + } +}
diff --git a/src/test/java/org/glassfish/el/test/ELProcessorTest.java b/src/test/java/org/glassfish/el/test/ELProcessorTest.java new file mode 100644 index 0000000..3381e7d --- /dev/null +++ b/src/test/java/org/glassfish/el/test/ELProcessorTest.java
@@ -0,0 +1,190 @@ +/* + * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.el.test; + +import org.junit.Test; +import org.junit.Before; +import org.junit.BeforeClass; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import javax.el.ELProcessor; +import javax.el.ELManager; +import javax.el.ExpressionFactory; +import javax.el.MethodExpression; +import javax.el.ELContext; +import java.lang.reflect.Method; + +public class ELProcessorTest { + + static ELProcessor elp; + static ELManager elm; + static ExpressionFactory factory; + + @BeforeClass + public static void setUpClass() throws Exception { + elp = new ELProcessor(); + elm = elp.getELManager(); + factory = elm.getExpressionFactory(); + } + + @Before + public void setUp() { + } + + @Test + public void testMethExpr() { + MethodExpression meth = null; + ELContext ctxt = elm.getELContext(); + try { + meth = factory.createMethodExpression( + ctxt, "#{str.length}", Object.class, null); + } catch (NullPointerException ex){ + // Do nothing + } + assertTrue(meth == null); + meth = factory.createMethodExpression( + ctxt, "#{'abc'.length()}", Object.class, null); + Object result = meth.invoke(ctxt, new Object[] {"abcde"}); + System.out.println("'abc'.length() called, equals " + result); + assertEquals(result, new Integer(3)); + } + + @Test + public void testGetValue() { + Object result = elp.eval("10 + 1"); + assertEquals(result.toString(), "11"); + result = elp.getValue("10 + 2", String.class); + assertEquals(result, "12"); + } + + @Test + public void testSetVariable () { + elp.setVariable("xx", "100"); + Object result = elp.getValue("xx + 11", String.class); + assertEquals(result, "111"); + elp.setVariable("xx", null); + assertEquals(elp.eval("xx"), null); + elp.setVariable("yy", "abc"); + assertEquals(elp.eval("yy = 123; abc"), 123L); + assertEquals(elp.eval("abc = 456; yy"), 456L); + } + + @Test + public void testConcat() { + Object result = elp.eval("'10' + 1"); + assertEquals(result, 11L); + result = elp.eval("10 += '1'"); + assertEquals(result.toString(), "101"); + } + + @Test + public void defineFuncTest() { + Class c = MyBean.class; + Method meth = null; + Method meth2 = null; + try { + meth = c.getMethod("getBar", new Class<?>[] {}); + meth2 = c.getMethod("getFoo", new Class<?>[] {}); + } catch (Exception e) { + System.out.printf("Exception: ", e); + } + try { + elp.defineFunction("xx", "", meth); + Object ret = elp.eval("xx:getBar() == 64"); + assertTrue((Boolean)ret); + } catch (NoSuchMethodException ex) { + + } + + boolean caught = false; + try { + elp.defineFunction("", "", meth2); + Object ret = elp.eval("getFoo() == 100"); + assertTrue((Boolean)ret); + } catch (NoSuchMethodException ex) { + caught = true; + } + assertTrue(caught); + + try { + elp.defineFunction("yy", "", "org.glassfish.el.test.ELProcessorTest$MyBean", "getBar"); + Object ret = elp.eval("yy:getBar() == 64"); + assertTrue((Boolean)ret); + } catch (ClassNotFoundException | NoSuchMethodException ex) { + + } + + caught = false; + try { + elp.defineFunction("yy", "", "org.glassfish.el.test.ELProcessorTest$MyBean", "getFooBar"); + Object ret = elp.eval("yy:getBar() == 100"); + assertTrue((Boolean)ret); + } catch (ClassNotFoundException | NoSuchMethodException ex) { + caught = true; + } + assertTrue(caught); + + caught = false; + try { + elp.defineFunction("yy", "", "testBean", "getFoo"); + Object ret = elp.eval("yy:getBar() == 100"); + assertTrue((Boolean)ret); + } catch (ClassNotFoundException | NoSuchMethodException ex) { + caught = true; + } + assertTrue(caught); + } +/* + @Test + public void testBean() { + elp.defineBean("xyz", new MyBean()); + Object result = elp.eval("xyz.foo"); + assertEquals(result.toString(), "100"); + } +*/ + + @Test + public void testImport() { + elm.importClass("org.glassfish.el.test.ELProcessorTest$MyBean"); + assertTrue((Boolean)elp.eval("ELProcessorTest$MyBean.aaaa == 101")); + assertTrue((Boolean)elp.eval("ELProcessorTest$MyBean.getBar() == 64")); + elm.importStatic("org.glassfish.el.test.ELProcessorTest$MyBean.aaaa"); + assertEquals(new Integer(101), elp.eval("aaaa")); + elm.importStatic("org.glassfish.el.test.ELProcessorTest$MyBean.getBar"); + assertEquals(new Integer(64), elp.eval("getBar()")); + /* + elm.importStatic("a.b.NonExisting.foobar"); + elp.eval("foobar"); + elp.eval("ELProcessorTest$MyBean.getFoo()"); + */ + } + + static public class MyBean { + public static int aaaa = 101; + public int getFoo() { + return 100; + } + public int getFoo(int i) { + return 200; + } + public static int getBar() { + return 64; + } + } +} +
diff --git a/src/test/java/org/glassfish/el/test/ElasticityTest.java.sav b/src/test/java/org/glassfish/el/test/ElasticityTest.java.sav new file mode 100644 index 0000000..3da7dce --- /dev/null +++ b/src/test/java/org/glassfish/el/test/ElasticityTest.java.sav
@@ -0,0 +1,146 @@ +/* + * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.el.test; + +import java.util.*; +import javax.el.*; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + * @author kichung + */ +public class ElasticityTest { + + ELProcessor elp; + + public ElasticityTest() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + @Before + public void setUp() { + elp = new ELProcessor(); + } + + @After + public void tearDown() { + } + + static public class Data { + int s; + int d; + + public Data(int s, int d) { + this.s = s; + this.d = d; + } + + public int getS() { + return this.s; + } + + public int getD() { + return this.d; + } + } + + static public class Metric { + int limit; + List<Data> list = new ArrayList<Data>(); + + public Metric(int limit) { + this.limit = limit; + } + + public int getLimit() { + return limit; + } + + public List<Data> getList() { + return list; + } + } + Map<String, Metric> clusters = new HashMap<String, Metric>(); + + private void init() { + Metric m1 = new Metric(10); + m1.getList().add(new Data(1, 80)); + m1.getList().add(new Data(3, 90)); + m1.getList().add(new Data(4, 100)); + m1.getList().add(new Data(5, 50)); + m1.getList().add(new Data(6, 60)); + + Metric m2 = new Metric(10); + m2.getList().add(new Data(1, 80)); + m2.getList().add(new Data(3, 82)); + m2.getList().add(new Data(7, 90)); + m2.getList().add(new Data(9, 140)); + m2.getList().add(new Data(15, 80)); + + Metric m3 = new Metric(10); + m3.getList().add(new Data(4, 100)); + m3.getList().add(new Data(5, 81)); + m3.getList().add(new Data(6, 200)); + m3.getList().add(new Data(20, 80)); + + clusters.put("c1", m1); + clusters.put("c2", m2); + clusters.put("c3", m3); + + elp.defineBean("c", clusters); + } + @Test + public void testElaticity() { + init(); + Object obj; + + obj = elp.eval( + "c.values().select(" + + "v->v.list.where(d->d.s>1 && d.s<10)." + + "average(d->d.d)).toList()"); + + System.out.println(obj); + obj = elp.eval( + "c.values().select(v->v.list." + + "where(d->d.s>1 && d.s<10)." + + "average(d->d.d) > 100).toList()"); + System.out.println(obj); + obj = elp.eval( + "c.values().select(v->v.list." + + "where(d->d.s>1 && d.s<10)." + + "average(d->d.d) > 100).any()"); + System.out.println(obj); + obj = elp.eval( + "c.entrySet().select(s->[s.key, s.value.list." + + "where(d->d.s>1 && d.s<10)." + + "average(d->d.d)]).toList()"); + System.out.println(obj); + } +}
diff --git a/src/test/java/org/glassfish/el/test/EscapingTest.java b/src/test/java/org/glassfish/el/test/EscapingTest.java new file mode 100644 index 0000000..4f78bd7 --- /dev/null +++ b/src/test/java/org/glassfish/el/test/EscapingTest.java
@@ -0,0 +1,71 @@ +/* + * Copyright (c) 2021 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.el.test; + +import javax.el.ELManager; +import javax.el.ELProcessor; +import javax.el.ValueExpression; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class EscapingTest { + + static ELProcessor elp; + static ELManager elm; + + public EscapingTest() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + elp = new ELProcessor(); + elm = elp.getELManager(); + } + + @Test + public void testEscape01() { + assertEquals("$2", evaluateExpression("$${1+1}")); + assertEquals("$${1+1}", evaluateExpression("$\\${1+1}")); + } + + @Test + public void testEscape02() { + assertEquals("$2", evaluateExpression("$#{1+1}")); + assertEquals("$#{1+1}", evaluateExpression("$\\#{1+1}")); + } + + @Test + public void testEscape03() { + assertEquals("#2", evaluateExpression("##{1+1}")); + assertEquals("##{1+1}", evaluateExpression("#\\#{1+1}")); + } + + @Test + public void testEscape04() { + assertEquals("#2", evaluateExpression("#${1+1}")); + assertEquals("#${1+1}", evaluateExpression("#\\${1+1}")); + } + + private String evaluateExpression(String expr) { + ValueExpression v = ELManager.getExpressionFactory().createValueExpression( + elm.getELContext(), expr, String.class); + return (String) v.getValue(elm.getELContext()); + } +}
diff --git a/src/test/java/org/glassfish/el/test/EvalListenerTest.java b/src/test/java/org/glassfish/el/test/EvalListenerTest.java new file mode 100644 index 0000000..410bde2 --- /dev/null +++ b/src/test/java/org/glassfish/el/test/EvalListenerTest.java
@@ -0,0 +1,110 @@ +/* + * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.el.test; + +import java.util.ArrayList; +import javax.el.ELManager; +import javax.el.ELContext; +import javax.el.ELProcessor; +import javax.el.EvaluationListener; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + * @author kichung + */ +public class EvalListenerTest { + + public EvalListenerTest() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + @Test + public void testEvalListener() { + ELProcessor elp = new ELProcessor(); + ELManager elm = elp.getELManager(); + final ArrayList<String> msgs = new ArrayList<String>(); + elm.addEvaluationListener(new EvaluationListener() { + @Override + public void beforeEvaluation(ELContext ctxt, String expr) { + System.out.println("Before: " + expr); + msgs.add("Before: " + expr); + } + @Override + public void afterEvaluation(ELContext ctxt, String expr) { + System.out.println("After: " + expr); + msgs.add("After: " + expr); + } + }); + elp.eval("100 + 10"); + elp.eval("x = 5; x*101"); + String[] expected = {"Before: ${100 + 10}", + "After: ${100 + 10}", + "Before: ${x = 5; x*101}", + "After: ${x = 5; x*101}" }; + for (int i = 0; i < expected.length; i++) { + assertEquals(expected[i], msgs.get(i)); + } + } + + @Test + public void testResListener() { + ELProcessor elp = new ELProcessor(); + ELManager elm = elp.getELManager(); + final ArrayList<String> msgs = new ArrayList<String>(); + elm.addEvaluationListener(new EvaluationListener() { + @Override + public void propertyResolved(ELContext ctxt, Object b, Object p) { + System.out.println("Resolved: " + b + " " + p); + msgs.add("Resolved: " + b + " " + p); + } + }); + elp.eval("x = 10"); + elp.eval("[1,2,3][2]"); + elp.eval("'abcd'.length()"); + elp.eval("'xyz'.class"); + String[] expected = { + "Resolved: null x", + "Resolved: [1, 2, 3] 2", + "Resolved: abcd length", + "Resolved: xyz class" + }; + for (int i = 0; i < expected.length; i++) { + assertEquals(expected[i], msgs.get(i)); + } + } +}
diff --git a/src/test/java/org/glassfish/el/test/LambdaTest.java b/src/test/java/org/glassfish/el/test/LambdaTest.java new file mode 100644 index 0000000..2734e0c --- /dev/null +++ b/src/test/java/org/glassfish/el/test/LambdaTest.java
@@ -0,0 +1,97 @@ +/* + * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.el.test; + +import org.junit.Test; +import org.junit.Before; +import org.junit.BeforeClass; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import javax.el.ELProcessor; + +public class LambdaTest { + + @BeforeClass + public static void setUpClass() throws Exception { + } + + @Before + public void setUp() { + } + + void testExpr(ELProcessor elp, String testname, String expr, Long expected) { + System.out.println("=== Test Lambda Expression:" + testname + " ==="); + System.out.println(" ** " + expr); + Object result = elp.eval(expr); + System.out.println(" returns " + result); + assertEquals(expected, result); + } + + @Test + public void testImmediate() { + ELProcessor elp = new ELProcessor(); + testExpr(elp, "immediate", "(x->x+1)(10)", 11L); + testExpr(elp, "immediate0", "(()->1001)()", 1001L); + testExpr(elp, "immediate1", "((x,y)->x+y)(null, null)", 0L); + testExpr(elp, "immediate 2", "(((x,y)->x+y)(3,4))", 7L); + testExpr(elp, "immediate 3", "(x->(y=x)+1)(10) + y", 21L); + } + + @Test + public void testAssignInvoke() { + ELProcessor elp = new ELProcessor(); + testExpr(elp, "assign", "func = x->x+1; func(10)", 11L); + testExpr(elp, "assign 2", "func = (x,y)->x+y; func(3,4)", 7L); + } + + @Test + public void testConditional() { + ELProcessor elp = new ELProcessor(); + elp.eval("cond = true"); + testExpr(elp, "conditional", "(x->cond? x+1: x+2)(10)", 11L); + elp.eval("cond = false"); + testExpr(elp, "conditional 2", + "func = cond? (x->x+1): (x->x+2); func(10)", 12L); + } + + @Test + public void testFact() { + ELProcessor elp = new ELProcessor(); + testExpr(elp, "factorial", "fact = n->n==0? 1: n*fact(n-1); fact(5)", 120L); + testExpr(elp, "fibonacci", "f = n->n==0? 0: n==1? 1: f(n-1)+f(n-2); f(10)", 55L); + } + + @Test + public void testVar() { + ELProcessor elp = new ELProcessor(); + elp.setVariable("v", "x->x+1"); + testExpr(elp, "assignment to variable", "v(10)", 11L); + } + + @Test + public void testLambda() { + ELProcessor elp = new ELProcessor(); + testExpr(elp, "Lambda Lambda", "f = ()->y->y+1; f()(100)", 101L); + testExpr(elp, "Lambda Lambda 2", "f = (x)->(tem=x; y->tem+y); f(1)(100)", 101L); + testExpr(elp, "Lambda Lambda 3", "(()->y->y+1)()(100)", 101L); + testExpr(elp, "Lambda Lambda 4", "(x->(y->x+y)(1))(100)", 101L); + testExpr(elp, "Lambda Lambda 5", "(x->(y->x+y))(1)(100)", 101L); + testExpr(elp, "Lambda Lambda 6" + , "(x->y->x(0)+y)(x->x+1)(100)", 101L); + } +}
diff --git a/src/test/java/org/glassfish/el/test/OperatorTest.java b/src/test/java/org/glassfish/el/test/OperatorTest.java new file mode 100644 index 0000000..a9d512f --- /dev/null +++ b/src/test/java/org/glassfish/el/test/OperatorTest.java
@@ -0,0 +1,127 @@ +/* + * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.el.test; + +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; +import javax.el.ELProcessor; +import javax.el.ELManager; +import javax.el.ExpressionFactory; +import javax.el.ValueExpression; +import javax.el.MethodExpression; + +/** + * + * @author Kin-man + */ +public class OperatorTest { + + static ELProcessor elp; + + public OperatorTest() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + elp = new ELProcessor(); + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + @Before + public void setUp() { + } + + void testExpr(String testname, String expr, Long expected) { + System.out.println("=== Test " + testname + " ==="); + System.out.println(" ** " + expr); + Object result = elp.eval(expr); + System.out.println(" returns " + result); + assertEquals(expected, result); + } + + void testExpr(String testname, String expr, String expected) { + System.out.println("=== Test " + testname + " ==="); + System.out.println(" ** " + expr); + Object result = elp.eval(expr); + System.out.println(" returns " + result); + assertEquals(expected, result); + } + + @Test + public void testConcat() { + testExpr("concat", "a = null; b = null; a + b", 0L); + testExpr("add", "10 + 11", 21L); + testExpr("concat", "'10' + 11", 21L); + testExpr("concat 2", "11 + '10'", 21L); + testExpr("concat 3", "100 += 10 ", "10010"); + testExpr("concat 4", "'100' += 10", "10010"); + testExpr("concat 5", "'100' + 10 + 1", 111L); + testExpr("concat 6", "'100' += 10 + 1", "10011"); + } + + @Test + public void testAssign() { + elp.eval("vv = 10"); + testExpr("assign", "vv+1", 11L); + elp.eval("vv = 100"); + testExpr("assign 2", "vv", 100L); + testExpr("assign 3", "x = vv = vv+1; x + vv", 202L); + elp.eval("map = {'one':100, 'two':200}"); + testExpr("assign 4", "map.two = 201; map.two", 201L); + testExpr("assign string", "x='string'; x += 1", "string1"); + } + + @Test + public void testSemi() { + testExpr("semi", "10; 20", 20L); + testExpr("semi0", "10; 20; 30", 30L); + elp.eval("x = 10; 20"); + testExpr("semi 2", "x", 10L); + testExpr("semi 3", "(x = 10; 20) + (x ; x+1)", 31L); + testExpr("semi 4", "(x = 10; y) = 11; x + y", 21L); + } + @Test + public void testMisc() { + testExpr("quote", "\"'\"", "'"); + testExpr("quote", "'\"'", "\""); + ELManager elm = elp.getELManager(); + ValueExpression v = elm.getExpressionFactory().createValueExpression( + elm.getELContext(), "#${1+1}", Object.class); + Object ret = v.getValue(elm.getELContext()); + assertEquals(ret, "#2"); + + elp.setVariable("debug", "true"); + ret = elp.eval("debug == true"); +// elp.eval("[1,2][true]"); // throws IllegalArgumentExpression +/* + elp.defineBean("date", new Date(2013, 1,2)); + elp.eval("date.getYear()"); + + elp.defineBean("href", null); + testExpr("space", "(empty href)?'#':href", "#"); + MethodExpression m = elm.getExpressionFactory().createMethodExpression( + elm.getELContext(), "${name}", Object.class, new Class[] {}); + m.invoke(elm.getELContext(), null); + */ + } +}
diff --git a/src/test/java/org/glassfish/el/test/Order.java b/src/test/java/org/glassfish/el/test/Order.java new file mode 100644 index 0000000..6ebfeaf --- /dev/null +++ b/src/test/java/org/glassfish/el/test/Order.java
@@ -0,0 +1,42 @@ +/* + * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.el.test; + +public class Order { + + int orderID; + int customerID; + Date orderDate; + double total; + + public Order(int orderID, int customerID, Date orderDate, double total) { + this.orderID = orderID; + this.customerID = customerID; + this.orderDate = orderDate; + this.total = total; + } + + public String toString() { + return "Order: " + orderID + ", " + customerID + + ", " + orderDate.toString() + ", " + total; + } + + public int getOrderID() { return orderID; } + public int getCustomerID() { return customerID; } + public Date getOrderDate() { return orderDate; } + public double getTotal() { return total; } +}
diff --git a/src/test/java/org/glassfish/el/test/OverloadedMethodTest.java b/src/test/java/org/glassfish/el/test/OverloadedMethodTest.java new file mode 100644 index 0000000..c0a3aa4 --- /dev/null +++ b/src/test/java/org/glassfish/el/test/OverloadedMethodTest.java
@@ -0,0 +1,279 @@ +/* + * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.el.test; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +import javax.el.*; + +/** + * + * @author Dongbin Nie + */ +public class OverloadedMethodTest { + + ELProcessor elp; + ExpressionFactory exprFactory; + ELContext elContext; + + @Before + public void setUp() { + elp = new ELProcessor(); + exprFactory = elp.getELManager().getExpressionFactory(); + elContext = elp.getELManager().getELContext(); + + elp.defineBean("foo", new MyBean()); + + elp.defineBean("i1", new I1Impl()); + elp.defineBean("i2", new I2Impl()); + elp.defineBean("i12", new I1AndI2Impl()); + elp.defineBean("i12s", new I1AndI2ImplSub()); + + } + + @After + public void tearDown() { + } + + @Test + public void testMethodWithNoArg() { + assertEquals("methodWithNoArg", elp.eval("foo.methodWithNoArg()")); + } + + @Test + public void testMethodNotExisted() { + try { + elp.eval("foo.methodNotExisted()"); + fail("testNoExistedMethod Failed"); + } catch (MethodNotFoundException e) { + } + } + + @Test + public void testMethodWithSingleArg() { + assertEquals("I1", elp.eval("foo.methodWithSingleArg(i1)")); + assertEquals("I2Impl", elp.eval("foo.methodWithSingleArg(i2)")); + assertEquals("I1AndI2Impl", elp.eval("foo.methodWithSingleArg(i12)")); + } + + @Test + public void testMethodWithDoubleArgs() { + assertEquals("I1Impl, I2", elp.eval("foo.methodWithDoubleArgs(i1, i2)")); + assertEquals("I1, I2", elp.eval("foo.methodWithDoubleArgs(i12, i2)")); + assertEquals("I1AndI2Impl, I1AndI2Impl", elp.eval("foo.methodWithDoubleArgs(i12, i12)")); + assertEquals("I1AndI2Impl, I1AndI2Impl", elp.eval("foo.methodWithDoubleArgs(i12s, i12)")); + assertEquals("I1AndI2Impl, I1AndI2Impl", elp.eval("foo.methodWithDoubleArgs(i12s, i12s)")); + } + + @Test + public void testMethodWithAmbiguousArgs() { + assertEquals("I1AndI2Impl, I2", elp.eval("foo.methodWithAmbiguousArgs(i12, i2)")); + assertEquals("I1, I1AndI2Impl", elp.eval("foo.methodWithAmbiguousArgs(i1, i12)")); + try { + elp.eval("foo.methodWithAmbiguousArgs(i12, i12)"); + fail("testMethodWithAmbiguousArgs Failed"); + } catch (MethodNotFoundException e) { + } + } + + @Test + public void testMethodWithCoercibleArgs() { + assertEquals("String, String", elp.eval("foo.methodWithCoercibleArgs('foo', 'bar')")); + assertEquals("String, String", elp.eval("foo.methodWithCoercibleArgs(i1, i12)")); + + assertEquals("String, String", elp.eval("foo.methodWithCoercibleArgs2(i1, 12345678)")); + assertEquals("Integer, Integer", elp.eval("foo.methodWithCoercibleArgs2(12345678, 12345678)")); + } + + @Test + public void testMethodWithVarArgs() { + assertEquals("I1, I1...", elp.eval("foo.methodWithVarArgs(i1)")); + assertEquals("I1, I1...", elp.eval("foo.methodWithVarArgs(i1, i1)")); + assertEquals("I1, I1...", elp.eval("foo.methodWithVarArgs(i12, i1, i12)")); + + assertEquals("I1, I1AndI2Impl...", elp.eval("foo.methodWithVarArgs2(i1)")); + assertEquals("I1, I1AndI2Impl...", elp.eval("foo.methodWithVarArgs2(i12)")); + assertEquals("I1, I1...", elp.eval("foo.methodWithVarArgs2(i1, i1)")); + assertEquals("I1, I1AndI2Impl...", elp.eval("foo.methodWithVarArgs2(i1, i12)")); + } + + @Test + public void testExactVarArgs() { + String[] args = {"foo", "bar", "hello"}; + elp.defineBean("args", args); + assertEquals("foo,bar,hello,", elp.eval("foo.methodWithExactVarArgs('foo', 'bar', 'hello')")); + assertEquals("foo,foo,bar,hello,", elp.eval("foo.methodWithExactVarArgs('foo', args)")); + } + + @Test + public void testMethodInStdout() { + elp.defineBean("out", System.out); + elp.eval("out.println('hello!')"); + elp.eval("out.println(12345678)"); + } + + /** + * JSF may invoke MethodExpression which has parameter declared, + * but pass in no arguments (not null). + */ + @Test + public void testMethodExprInvokingWithoutArgs() { + MethodExpression methodExpr = exprFactory.createMethodExpression( + elContext, + "${foo.methodForMethodExpr}", + String.class, + new Class<?>[]{String.class}); + + assertNull(methodExpr.invoke(elContext, new Object[0])); + } + + @Test + public void testMethodExprInvoking() { + MethodExpression methodExpr = exprFactory.createMethodExpression( + elContext, + "${foo.methodForMethodExpr2}", + String.class, + new Class<?>[]{Runnable.class}); + assertEquals("Runnable", methodExpr.invoke(elContext, new Object[]{Thread.currentThread()})); + try { + methodExpr.invoke(elContext, new Object[]{"foo"}); + fail("testMethodExprInvoking Failed"); + } catch (ELException e) { + System.out.println("The following is an expected exception:"); + e.printStackTrace(System.out); + } + } + + + public static interface I1 { + + } + + public static interface I2 { + + } + + public static class I1Impl implements I1 { + + } + + public static class I2Impl implements I2 { + + } + + public static class I1AndI2Impl implements I1, I2 { + + } + + public static class I1AndI2ImplSub extends I1AndI2Impl { + + } + + static public class MyBean { + + public String methodWithNoArg() { + return "methodWithNoArg"; + } + + public String methodWithSingleArg(I1 i1) { + return "I1"; + } + + public String methodWithSingleArg(I2 i2) { + return "I2"; + } + + public String methodWithSingleArg(I2Impl i2) { + return "I2Impl"; + } + + public String methodWithSingleArg(I1AndI2Impl i1) { + return "I1AndI2Impl"; + } + + public String methodWithDoubleArgs(I1 i1, I2 i2) { + return "I1, I2"; + } + + public String methodWithDoubleArgs(I1Impl i1, I2 i2) { + return "I1Impl, I2"; + } + + public String methodWithDoubleArgs(I1AndI2Impl i1, I1AndI2Impl i2) { + return "I1AndI2Impl, I1AndI2Impl"; + } + + public String methodWithAmbiguousArgs(I1AndI2Impl i1, I2 i2) { + return "I1AndI2Impl, I2"; + } + + public String methodWithAmbiguousArgs(I1 i1, I1AndI2Impl i2) { + return "I1, I1AndI2Impl"; + } + + public String methodWithCoercibleArgs(String s1, String s2) { + return "String, String"; + } + + public String methodWithCoercibleArgs2(String s1, String s2) { + return "String, String"; + } + + public String methodWithCoercibleArgs2(Integer s1, Integer s2) { + return "Integer, Integer"; + } + + public String methodWithVarArgs(I1 i1, I1... i2) { + return "I1, I1..."; + } + + public String methodWithVarArgs2(I1 i1, I1... i2) { + return "I1, I1..."; + } + + public String methodWithVarArgs2(I1 i1, I1AndI2Impl... i2) { + return "I1, I1AndI2Impl..."; + } + + public String methodWithExactVarArgs(String arg1, String... args) { + StringBuilder sb = new StringBuilder(); + sb.append(arg1).append(","); + for (String arg : args) { + sb.append(arg).append(","); + } + return sb.toString(); + } + + public String methodForMethodExpr(String arg1) { + return arg1; + } + + public String methodForMethodExpr2(Runnable r) { + return "Runnable"; + } + + public String methodForMethodExpr2(String s) { + return "String"; + } + + } + +}
diff --git a/src/test/java/org/glassfish/el/test/Product.java b/src/test/java/org/glassfish/el/test/Product.java new file mode 100644 index 0000000..72d9cd5 --- /dev/null +++ b/src/test/java/org/glassfish/el/test/Product.java
@@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.el.test; + +public class Product { + + public int productID; + public String name; + public String category; + public double unitPrice; + public int unitsInStock; + + Product (int productID, String name, String category, + double unitPrice, int unitsInStock) { + + this.productID = productID; + this.name = name; + this.category = category; + this.unitPrice = unitPrice; + this.unitsInStock = unitsInStock; + } + + public String toString() { + return "Product: " + productID + ", " + name + ", " + + category + ", " + unitPrice + ", " + unitsInStock; + } + + public int getProductID() { return productID; } + public String getName() { return name; } + public String getCategory() { return category; } + public double getUnitPrice() { return unitPrice; } + public int getUnitsInStock() { return unitsInStock; } + +}
diff --git a/src/test/java/org/glassfish/el/test/StaticRefTest.java b/src/test/java/org/glassfish/el/test/StaticRefTest.java new file mode 100644 index 0000000..4c8df7b --- /dev/null +++ b/src/test/java/org/glassfish/el/test/StaticRefTest.java
@@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.el.test; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; + +import javax.el.*; + +/** + * + * @author kichung + */ +public class StaticRefTest { + + ELProcessor elp; + + public StaticRefTest() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + @Before + public void setUp() { + elp = new ELProcessor(); + } + + @After + public void tearDown() { + } + + @Test + public void testStaticRef() { + // Pre imported java.lang classes +// assertTrue((Boolean)elp.eval("T(java.lang.Boolean).TRUE")); +// assertTrue((Boolean)elp.eval("T(Boolean).TRUE")); + assertTrue((Boolean)elp.eval("Boolean.TRUE")); + assertTrue((Boolean)elp.eval("Boolean.TRUE")); // test caching Boolean + } + +/* + @Test + public void testClass() { + assertEquals(String.class, elp.eval("String.class")); + } +*/ + + @Test + public void testConstructor() { +// assertEquals(new Integer(1001), elp.eval("T(Integer)(1001)")); + assertEquals(new Integer(1001), elp.eval("Integer(1001)")); + } + + @Test + public void testStaticMethod() { + assertEquals(4, elp.eval("Integer.numberOfTrailingZeros(16)")); + } +}
diff --git a/src/test/java/org/glassfish/el/test/StreamTest.java b/src/test/java/org/glassfish/el/test/StreamTest.java new file mode 100644 index 0000000..c37c71e --- /dev/null +++ b/src/test/java/org/glassfish/el/test/StreamTest.java
@@ -0,0 +1,282 @@ +/* + * Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0, which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.el.test; + +import java.lang.reflect.Array; +import java.util.List; +import java.util.Map; +import java.util.Iterator; + +import org.junit.Test; +import org.junit.Before; +import org.junit.BeforeClass; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import javax.el.ELProcessor; +import javax.el.ELException; + +public class StreamTest { + + static ELProcessor elp; + static DataBase database = null; + + @BeforeClass + public static void setUpClass() throws Exception { + elp = new ELProcessor(); + database = new DataBase(); + database.init(); + elp.defineBean("customers", database.getCustomers()); + elp.defineBean("products", database.getProducts()); + elp.defineBean("orders", database.getOrders()); + } + + @Before + public void setup() { + } + + void p(String msg) { + System.out.println(msg); + } + /** + * Test a collection query that returns an array, list or Iterable. + * @param name of the test + * @param query The EL query string + * @param expected The expected result of the array, list or Iterable. + * The array element should equal the elements in the array, list or + * Iterable, when enumerated. + */ + void testStream(String name, String query, String[] expected) { + p("=== Testing " + name + " ==="); + p(query); + Object ret = elp.eval(query); + p(" = returns ="); + + if (ret.getClass().isArray()) { + int size = Array.getLength(ret); + assertTrue(size == expected.length); + for (int i = 0; i < size; i++) { + Object item = Array.get(ret, i); + p(" " + item.toString()); + assertEquals(item.toString(), expected[i]); + } + return; + } + + if (ret instanceof List) { + List<Object> list = (List<Object>) ret; + int i = 0; + for (Object item: list) { + p(" " + item.toString()); + assertEquals(item.toString(), expected[i++]); + } + assertTrue(i == expected.length); + return; + } + + if (ret instanceof Iterator) { + int i = 0; + Iterator<Object> iter = (Iterator<Object>) ret; + while (iter.hasNext()) { + Object item = iter.next(); + p(" " + item.toString()); + assertEquals(item.toString(), expected[i++]); + } + assertTrue(i == expected.length); + return; + } + + // unexpected return type + assertTrue(false); + } + + void testStream(String name, String query, Object expected) { + p("=== Testing " + name + " ==="); + p(query); + Object ret = elp.eval(query); + p(" = returns " + ret + "(" + ret.getClass() + ")"); + assertEquals(ret, expected); + p(""); + } + + static String[] exp0 = {"1", "2", "3", "4", "5", "6"}; + static String[] exp1 = {"6", "5", "4", "3", "2", "1"}; + static String[] exp2 = {"q", "z", "yz", "aaa", "abc", "xyz"}; + static String[] exp3 = {"2", "3", "4"}; + static String[] exp4 = {"20", "30", "40"}; + + @Test + public void testFilterMap() { + testStream("filter", "[1,2,3,4].stream().filter(i->i > 1).toList()", exp3); + testStream("map", "[2,3,4].stream().map(i->i*10).iterator()", exp4); + testStream("filtermap", "[1,2,3,4].stream().filter(i->i > 1)\n" + + " .map(i->i*10).toArray()", exp4); + } + + static String[] exp5 = { + "Product: 201, Coming Home, dvd, 8.0, 50", + "Product: 200, Eagle, book, 12.5, 100", + "Product: 202, Greatest Hits, cd, 6.5, 200", + "Product: 203, History of Golf, book, 11.0, 30", + "Product: 204, Toy Story, dvd, 10.0, 1000", + "Product: 205, iSee, book, 12.5, 150"}; + + static String[] exp6 = { + "Product: 203, History of Golf, book, 11.0, 30", + "Product: 200, Eagle, book, 12.5, 100", + "Product: 205, iSee, book, 12.5, 150", + "Product: 202, Greatest Hits, cd, 6.5, 200", + "Product: 201, Coming Home, dvd, 8.0, 50", + "Product: 204, Toy Story, dvd, 10.0, 1000"}; + + @Test + public void testSorted() { + testStream("distinct", "[2, 3, 2, 4, 4].stream().distinct().toList()", exp3); + testStream("sorted", "[1, 3, 5, 2, 4, 6].stream().sorted().toList()", exp0); + testStream("sorted", "[1, 3, 5, 2, 4, 6].stream().sorted((i,j)->i-j).toList()", exp0); + testStream("sorted", "[1, 3, 5, 2, 4, 6].stream().sorted((i,j)->i.compareTo(j)).toList()", exp0); + testStream("sorted", "['2', '4', '6', '5', '3', '1'].stream().sorted((s, t)->s.compareTo(t)).toList()", exp0); + testStream("sorted", "[1, 3, 5, 2, 4, 6].stream().sorted((i,j)->j.compareTo(i)).toList()", exp1); + testStream("sorted", "['xyz', 'yz', 'z', 'abc', 'aaa', 'q'].stream().sorted" + + "((s,t)->(s.length()== t.length()? s.compareTo(t): s.length() - t.length())).toList()", + exp2); + elp.eval("comparing = map->(x,y)->map(x).compareTo(map(y))"); + testStream("sorted", "products.stream().sorted(" + + "(x,y)->x.name.compareTo(y.name)).toList()", exp5); + testStream("sorted", "products.stream().sorted(" + + "comparing(p->p.name)).toList()", exp5); + elp.eval("compose = (m1,m2)->(x,y)->(tx = m1(x).compareTo(m1(y)); " + + "tx!=0? tx: (m2(x).compareTo(m2(y))))"); + testStream("sorted", "products.stream().sorted(" + + "compose(p->p.category, p->p.unitPrice)).toList()", exp6); + } + + static String exp8[] = {"Eagle", "Coming Home", "Greatest Hits", + "History of Golf", "Toy Story" , "iSee"}; + + String exp11[] = {"1","2","3","4"}; + @Test + public void testForEach() { + testStream("forEach", + "lst = []; products.stream().forEach(p->lst.add(p.name)); lst", exp8); + testStream("peek", + "lst = []; [1,2,3,4].stream().peek(i->lst.add(i)).toList()", exp11); + testStream("peek2", "lst", exp11); + } + + static String[] exp7 = { + "Order: 10, 100, 2/18/2010, 20.8", + "Order: 11, 100, 5/3/2011, 34.5", + "Order: 12, 100, 8/2/2011, 210.75", + "Order: 13, 101, 1/15/2011, 50.23", + "Order: 14, 101, 1/3/2012, 126.77"}; + + static String[] exp9 = {"t","h","e","q","u","i","c","k","b","r","o","w","n","f","o","x"}; + + @Test + public void testFlapMap() { + testStream("flatMap", + "customers.stream().filter(c->c.country=='USA')\n" + + " .flatMap(c->c.orders.stream()).toList()", + exp7); + testStream("flatMap String", + "['the', 'quick', 'brown', 'fox']" + + ".stream().flatMap(s->s.toCharArray().stream()).toList()", + exp9); + } + + static String exp10[] = {"0", "1", "2"}; + + @Test + public void testSubstream() { + testStream("limit", "[0,1,2,3,4,5].stream().limit(3).toList()", exp10); + testStream("substream", "[0,1,2,3,4].stream().substream(2).toList()", exp3); + testStream("substream", "[0,1,2,3,4,5,6].stream().substream(2,5).toList()", exp3); + } + + @Test + public void testReduce() { + testStream("reduce", "[1,2,3,4,5].stream().reduce(0, (l,r)->l+r)", Long.valueOf(15)); + testStream("reduce", "[1,2,3,4,5].stream().reduce((l,r)->l+r).get()", Long.valueOf(15)); + testStream("reduce", "[].stream().reduce((l,r)->l+r).orElse(101)", Long.valueOf(101)); + testStream("reduce", "[].stream().reduce((l,r)->l+r).orElseGet(()->101)", Long.valueOf(101)); + testStream("reduce", "c = 0; [1,2,3,4,5,6].stream().reduce(0, (l,r)->(c = c+1; c % 2 == 0? l+r: l-r))", Long.valueOf(3)); + } + + @Test + public void testMatch() { + testStream("anyMatch", "[1,2,3,4].stream().anyMatch(e->e == 3)", Boolean.TRUE); + testStream("anyMatch", "[1,2,3,4].stream().anyMatch(e->e > 10)", Boolean.FALSE); + testStream("allMatch", "[1,2,3,4].stream().allMatch(e->e > 0)", Boolean.TRUE); + testStream("allMatch", "[1,2,3,4].stream().allMatch(e->e > 1)", Boolean.FALSE); + testStream("noneMatch", "[1,2,3,4].stream().noneMatch(e->e > 1)", Boolean.FALSE); + testStream("noneMatch", "[1,2,3,4].stream().noneMatch(e->e > 10)", Boolean.TRUE); + } + + @Test + public void testToType() { + testStream("toArray", "[2,3,4].stream().map(i->i*10).toArray()", exp4); + testStream("toList", "[2,3,4].stream().map(i->i*10).toList()", exp4); + testStream("Iterator", "[2,3,4].stream().map(i->i*10).iterator()", exp4); + } + + @Test + public void testFind() { + testStream("findFirst", "[101, 100].stream().findFirst().get()", Long.valueOf(101)); + boolean caught = false; + try { + elp.eval("[].stream().findFirst().get()"); + } catch (ELException ex) { + caught = true; + } + assertTrue(caught); + testStream("findFirst", "[101, 100].stream().findFirst().isPresent()", Boolean.TRUE); + testStream("findFirst", "[].stream().findFirst().isPresent()", Boolean.FALSE); + } + + @Test + public void testArith() { + testStream("sum", "[1,2,3,4,5].stream().sum()", Long.valueOf(15)); + testStream("sum", "[1.4,2,3,4,5.1].stream().sum()", Double.valueOf(15.5)); + testStream("average", "[1,2,3,4,5].stream().average().get()", Double.valueOf(3.0)); + testStream("average", "[1.4,2,3,4,5.1].stream().average().get()", Double.valueOf(3.1)); + testStream("count", "[1,2,3,4,5].stream().count()", Long.valueOf(5)); + } + + @Test + public void testMinMax() { + testStream("min", "[2,3,1,5].stream().min().get()", Long.valueOf(1)); + testStream("max", "[2,3,1,5].stream().max().get()", Long.valueOf(5)); + testStream("max", "['xy', 'xyz', 'abc'].stream().max().get()", "xyz"); + testStream("max", "[2].stream().max((i,j)->i-j).get()", Long.valueOf(2)); + elp.eval("comparing = map->(x,y)->map(x).compareTo(map(y))"); + testStream("max", "customers.stream().max((x,y)->x.orders.size()-y.orders.size()).get().name", "John Doe"); + testStream("max", "customers.stream().max(comparing(c->c.orders.size())).get().name", "John Doe"); + testStream("min", "[3,2,1].stream().min((i,j)->i-j).get()", Long.valueOf(1)); + testStream("min", "customers.stream().min((x,y)->x.orders.size()-y.orders.size()).get().name", "Charlie Yeh"); + elp.eval("comparing = map->(x,y)->map(x).compareTo(map(y))"); + testStream("min", "customers.stream().min(comparing(c->c.orders.size())).get().name", "Charlie Yeh"); + } + + @Test + public void testMap() { + Object r = elp.eval("v = {'one':1, 'two':2}"); + System.out.println(" "+ r); + r = elp.eval("{1,2,3}"); + System.out.println(" "+ r); + } +}
diff --git a/uel/.gitkeep b/uel/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/uel/.gitkeep