[
  {
    "path": ".gitattributes",
    "content": "# Set the default behavior, in case people don't have core.autocrlf set.\n* text=auto\n\n# Declare OSX files that will always have LF line endings on checkout.\nInfo.plist text eol=lf\n\n# Declare script files that will always have LF line endings on checkout.\n*.sh text eol=lf\n\n# Declare script files that will always have CR/LF line endings on checkout.\n*.bat text eol=crlf\n\n# Denote all files that are truly binary and should not be modified.\n*.png binary\n*.jpg binary\n*.gif binary\n*.icns binary\n"
  },
  {
    "path": ".gitignore",
    "content": "# Java\n*.class\n\n# JD\ndebug*\n\n# JD-GUI\nsrc-generated/\njd-gui.cfg\n\n# Idea\n.idea/\nout/\n*.ipr\n*.iml\n*.iws\n\n# Eclipse\n.settings/\nclasses/\n.classpath\n.project\n\n# Mac\n.DS_Store\n\n#Windows\nThumbs.db\n\n# Maven\nlog/\ntarget/\n\n# Gradle\n.gradle/\nbuild/\n!gradle/wrapper/*\n\n# WinMerge\n*.bak\n"
  },
  {
    "path": "LICENSE",
    "content": "                     GNU GENERAL PUBLIC LICENSE\n\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    JD-GUI, a standalone graphical utility that displays Java sources from\n\tCLASS files\n    Copyright (C) 2008-2019  Emmanuel Dupuy\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    JD-GUI  Copyright (C) 2008-2019  Emmanuel Dupuy\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<http://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<http://www.gnu.org/philosophy/why-not-lgpl.html>.\n"
  },
  {
    "path": "NOTICE",
    "content": "JD-GUI license - GPLv3\n\nLibraries used:\n\nGroovy - Apache License 2.0\nGradle - Apache License 2.0\nJD-Core Java Release - GPLv3\nRSyntaxTextArea - Modified BSD license\n\nJD-GUI Mac OSX distribution:\n\nuniversalJavaApplicationStub - MIT License\n\nJD-GUI Windows distribution:\n\nLaunch4j - MIT License\n"
  },
  {
    "path": "README.md",
    "content": "# JD-GUI\n\nJD-GUI, a standalone graphical utility that displays Java sources from CLASS files.\n\n![](https://raw.githubusercontent.com/java-decompiler/jd-gui/master/src/website/img/jd-gui.png)\n\n- Java Decompiler projects home page: [http://java-decompiler.github.io](http://java-decompiler.github.io)\n- JD-GUI source code: [https://github.com/java-decompiler/jd-gui](https://github.com/java-decompiler/jd-gui)\n\n## Description\nJD-GUI is a standalone graphical utility that displays Java source codes of \n\".class\" files. You can browse the reconstructed source code with the JD-GUI\nfor instant access to methods and fields.\n\n## How to build JD-GUI ?\n```\n> git clone https://github.com/java-decompiler/jd-gui.git\n> cd jd-gui\n> ./gradlew build \n```\ngenerate :\n- _\"build/libs/jd-gui-x.y.z.jar\"_\n- _\"build/libs/jd-gui-x.y.z-min.jar\"_\n- _\"build/distributions/jd-gui-windows-x.y.z.zip\"_\n- _\"build/distributions/jd-gui-osx-x.y.z.tar\"_\n- _\"build/distributions/jd-gui-x.y.z.deb\"_\n- _\"build/distributions/jd-gui-x.y.z.rpm\"_\n\n## How to launch JD-GUI ?\n- Double-click on _\"jd-gui-x.y.z.jar\"_\n- Double-click on _\"jd-gui.exe\"_ application from Windows\n- Double-click on _\"JD-GUI\"_ application from Mac OSX\n- Execute _\"java -jar jd-gui-x.y.z.jar\"_ or _\"java -classpath jd-gui-x.y.z.jar org.jd.gui.App\"_\n\n## How to use JD-GUI ?\n- Open a file with menu \"File > Open File...\"\n- Open recent files with menu \"File > Recent Files\"\n- Drag and drop files from your file explorer\n\n## How to extend JD-GUI ?\n```\n> ./gradlew idea \n```\ngenerate Idea Intellij project\n```\n> ./gradlew eclipse\n```\ngenerate Eclipse project\n```\n> java -classpath jd-gui-x.y.z.jar;myextension1.jar;myextension2.jar org.jd.gui.App\n```\nlaunch JD-GUI with your extensions\n\n## How to uninstall JD-GUI ?\n- Java: Delete \"jd-gui-x.y.z.jar\" and \"jd-gui.cfg\".\n- Mac OSX: Drag and drop \"JD-GUI\" application into the trash.\n- Windows: Delete \"jd-gui.exe\" and \"jd-gui.cfg\".\n\n## License\nReleased under the [GNU GPL v3](LICENSE).\n\n## Donations\nDid JD-GUI help you to solve a critical situation? Do you use JD-Eclipse daily? What about making a donation?\n\n[![paypal](https://raw.githubusercontent.com/java-decompiler/jd-gui/master/src/website/img/btn_donate_euro.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=C88ZMVZ78RF22) [![paypal](https://raw.githubusercontent.com/java-decompiler/jd-gui/master/src/website/img/btn_donate_usd.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=CRMXT4Y4QLQGU)\n"
  },
  {
    "path": "api/build.gradle",
    "content": "apply plugin: 'java'\n\nversion = '1.0.0'\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/api/API.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.api;\n\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.spi.*;\n\nimport javax.swing.*;\nimport java.io.File;\nimport java.net.URI;\nimport java.nio.file.Path;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.concurrent.Future;\n\npublic interface API {\n    boolean openURI(URI uri);\n\n    boolean openURI(int x, int y, Collection<Container.Entry> entries, String query, String fragment);\n\n    void addURI(URI uri);\n\n    <T extends JComponent & UriGettable> void addPanel(String title, Icon icon, String tip, T component);\n\n    Collection<Action> getContextualActions(Container.Entry entry, String fragment);\n\n    UriLoader getUriLoader(URI uri);\n\n    FileLoader getFileLoader(File file);\n\n    ContainerFactory getContainerFactory(Path rootPath);\n\n    PanelFactory getMainPanelFactory(Container container);\n\n    TreeNodeFactory getTreeNodeFactory(Container.Entry entry);\n\n    TypeFactory getTypeFactory(Container.Entry entry);\n\n    Indexer getIndexer(Container.Entry entry);\n\n    SourceSaver getSourceSaver(Container.Entry entry);\n\n    Map<String, String> getPreferences();\n\n    Collection<Future<Indexes>> getCollectionOfFutureIndexes();\n\n    interface LoadSourceListener {\n        void sourceLoaded(String source);\n    }\n\n    String getSource(Container.Entry entry);\n\n    void loadSource(Container.Entry entry, LoadSourceListener listener);\n\n    File loadSourceFile(Container.Entry entry);\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/api/feature/ContainerEntryGettable.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.api.feature;\n\nimport org.jd.gui.api.model.Container;\n\npublic interface ContainerEntryGettable {\n    Container.Entry getEntry();\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/api/feature/ContentCopyable.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.api.feature;\n\npublic interface ContentCopyable {\n    void copy();\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/api/feature/ContentIndexable.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.api.feature;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Indexes;\n\npublic interface ContentIndexable {\n    Indexes index(API api);\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/api/feature/ContentSavable.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.api.feature;\n\nimport org.jd.gui.api.API;\n\nimport java.io.OutputStream;\n\npublic interface ContentSavable {\n    String getFileName();\n\n    void save(API api, OutputStream os);\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/api/feature/ContentSearchable.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.api.feature;\n\npublic interface ContentSearchable {\n    boolean highlightText(String text, boolean caseSensitive);\n\n    void findNext(String text, boolean caseSensitive);\n\n    void findPrevious(String text, boolean caseSensitive);\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/api/feature/ContentSelectable.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.api.feature;\n\npublic interface ContentSelectable {\n    void selectAll();\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/api/feature/FocusedTypeGettable.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.api.feature;\n\npublic interface FocusedTypeGettable extends ContainerEntryGettable {\n    String getFocusedTypeName();\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/api/feature/IndexesChangeListener.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.api.feature;\n\nimport org.jd.gui.api.model.Indexes;\n\nimport java.util.Collection;\nimport java.util.concurrent.Future;\n\npublic interface IndexesChangeListener {\n    void indexesChanged(Collection<Future<Indexes>> collectionOfFutureIndexes);\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/api/feature/LineNumberNavigable.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.api.feature;\n\npublic interface LineNumberNavigable {\n    int getMaximumLineNumber();\n\n    void goToLineNumber(int lineNumber);\n\n    boolean checkLineNumber(int lineNumber);\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/api/feature/PageChangeListener.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.api.feature;\n\nimport javax.swing.*;\n\npublic interface PageChangeListener {\n    <T extends JComponent & UriGettable> void pageChanged(T page);\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/api/feature/PageChangeable.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.api.feature;\n\npublic interface PageChangeable {\n    void addPageChangeListener(PageChangeListener listener);\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/api/feature/PageClosable.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.api.feature;\n\npublic interface PageClosable {\n    boolean closePage();\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/api/feature/PageCreator.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.api.feature;\n\nimport org.jd.gui.api.API;\n\nimport javax.swing.*;\n\npublic interface PageCreator {\n    <T extends JComponent & UriGettable> T createPage(API api);\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/api/feature/PreferencesChangeListener.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.api.feature;\n\nimport java.util.Map;\n\npublic interface PreferencesChangeListener {\n    void preferencesChanged(Map<String, String> preferences);\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/api/feature/SourcesSavable.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.api.feature;\n\nimport org.jd.gui.api.API;\n\nimport java.nio.file.Path;\n\npublic interface SourcesSavable {\n    String getSourceFileName();\n\n    int getFileCount();\n\n    void save(API api, Controller controller, Listener listener, Path path);\n\n    interface Controller {\n        boolean isCancelled();\n    }\n\n    interface Listener {\n        void pathSaved(Path path);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/api/feature/TreeNodeExpandable.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.api.feature;\n\nimport org.jd.gui.api.API;\n\npublic interface TreeNodeExpandable {\n    void populateTreeNode(API api);\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/api/feature/UriGettable.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.api.feature;\n\nimport java.net.URI;\n\npublic interface UriGettable {\n    URI getUri();\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/api/feature/UriOpenable.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.api.feature;\n\nimport java.net.URI;\n\n/**\n * uri                : scheme '://' path ('?' query)? ('#' fragment)?<br>\n * scheme             : 'generic' | 'jar' | 'war' | 'ear' | 'dex' | ...<br>\n * path               : singlePath('!' singlePath)*<br>\n * singlePath         : [path/to/dir/] | [path/to/file]<br>\n * query              : queryLineNumber | queryPosition | querySearch<br>\n * queryLineNumber    : 'lineNumber=' [numeric]<br>\n * queryPosition      : 'position=' [numeric]<br>\n * querySearch        : 'highlightPattern=' queryPattern '&highlightFlags=' queryFlags ('&highlightScope=' typeName)?<br>\n * queryPattern       : [start of string] | [start of type name] | [start of field name] | [start of method name]<br>\n * queryFlags         : 'd'? // Match declarations<br>\n *                      'r'? // Match references<br>\n *                      't'? // Match types<br>\n *                      'c'? // Match constructors<br>\n *                      'm'? // Match methods<br>\n *                      'f'? // Match fields<br>\n *                      's'? // Match strings<br>\n * fragment            : fragmentType | fragmentField | fragmentMethod<br>\n * fragmentType        : typeName<br>\n * fragmentField       : typeName '-' [field name] '-' descriptor<br>\n * fragmentMethod      : typeName '-' [method name] '-' methodDescriptor<br>\n * methodDescriptor    : '(*)?' | // Match all method descriptors<br>\n *                       '(' descriptor* ')' descriptor<br>\n * descriptor          : '?' | // Match a primitive or a type name<br>\n *                       '['* primitiveOrTypeName<br>\n * primitiveOrTypeName : 'B' | 'C' | 'D' | 'F' | 'I' | 'J' | 'L' typeName ';' | 'S' | 'Z'<br>\n * typeName            : [internal qualified name] | '*\\/' [name]<br>\n * <br>\n * Examples:<br>\n * <ul>\n *  <li>file://dir1/dir2/</li>\n *  <li>file://dir1/dir2/file</li>\n *  <li>jar://dir1/dir2/</li>\n *  <li>jar://dir1/dir2/file</li>\n *\n *  <li>jar://dir1/dir2/javafile</li>\n *  <li>jar://dir1/dir2/javafile#type</li>\n *  <li>jar://dir1/dir2/javafile#type-fieldName-descriptor</li>\n *  <li>jar://dir1/dir2/javafile#type-methodName-descriptor</li>\n *  <li>jar://dir1/dir2/javafile#innertype</li>\n *  <li>jar://dir1/dir2/javafile#innertype-fieldName-?</li>\n *  <li>jar://dir1/dir2/javafile#innertype-methodName-(*)?</li>\n *  <li>jar://dir1/dir2/javafile#innertype-methodName-(?JZLjava/lang/Sting;C)I</li>\n *  <li>jar://dir1/dir2/javafile#innertype-fieldName-descriptor</li>\n *  <li>jar://dir1/dir2/javafile#innertype-methodName-descriptor</li>\n *\n *  <li>file://dir1/dir2/file?lineNumber=numeric</li>\n *  <li>file://dir1/dir2/file?position=numeric</li>\n *  <li>file://dir1/dir2/file?highlightPattern=hello&highlightFlags=drtcmfs&highlightScope=java/lang/String</li>\n *  <li>file://dir1/dir2/file?highlightPattern=hello&highlightFlags=drtcmfs&highlightScope=*\\/String</li>\n * </ul>\n */\npublic interface UriOpenable {\n    boolean openUri(URI uri);\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/api/model/Container.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.api.model;\n\nimport java.io.InputStream;\nimport java.net.URI;\nimport java.util.Collection;\n\npublic interface Container {\n    String getType();\n\n    Entry getRoot();\n\n    /**\n     * File or directory\n     */\n    interface Entry {\n        Container getContainer();\n\n        Entry getParent();\n\n        URI getUri();\n\n        String getPath();\n\n        boolean isDirectory();\n\n        long length();\n\n        InputStream getInputStream();\n\n        Collection<Entry> getChildren();\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/api/model/Indexes.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.api.model;\n\nimport java.util.Collection;\nimport java.util.Map;\n\n/**\n * Whatever the language/file format (Java|Groovy|Scala/Class|DEX, Java|Javascript/Source, C#/CIL, ...), type names,\n * stored in the indexes, use the JVM internal format (package separator = '/', inner class separator = '$').<br>\n * <br>\n * List of default indexes:\n * <ul>\n *     <li>\n *         Map \"strings\"<br>\n *         key: a string<br>\n *         value: a list of entries containing the string\n *     </li>\n *     <li>\n *         Map \"typeDeclarations\"<br>\n *         key: a type name using internal JVM internal format<br>\n *         value: a list of entries containing the type declaration\n *     </li>\n *     <li>\n *         Map \"constructorDeclarations\"<br>\n *         key: a type name using internal JVM internal format<br>\n *         value: a list of entries containing the constructor declaration\n *     </li>\n *     <li>\n *         Map \"constructorReferences\"<br>\n *         key: a type name using internal JVM internal format<br>\n *         value: a list of entries containing the constructor reference\n *     </li>\n *     <li>\n *         Map \"methodDeclarations\"<br>\n *         key: a method name<br>\n *         value: a list of entries containing the method declaration\n *     </li>\n *     <li>\n *         Map \"methodReferences\"<br>\n *         key: a method name<br>\n *         value: a list of entries containing the method reference\n *     </li>\n *     <li>\n *         Map \"fieldDeclarations\"<br>\n *         key: a field name<br>\n *         value: a list of entries containing the field declaration\n *     </li>\n *     <li>\n *         Map \"fieldReferences\"<br>\n *         key: a field name<br>\n *         value: a list of entries containing the field reference\n *     </li>\n *     <li>\n *         Map \"subTypeNames\"<br>\n *         key: a super type name using internal JVM internal format<br>\n *         value: a list of sub type names using internal JVM internal format\n *     </li>\n * </ul>\n */\npublic interface Indexes {\n    Map<String, Collection> getIndex(String name);\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/api/model/TreeNodeData.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.api.model;\n\nimport javax.swing.*;\n\npublic interface TreeNodeData {\n    String getLabel();\n\n    String getTip();\n\n    Icon getIcon();\n\n    Icon getOpenIcon();\n}"
  },
  {
    "path": "api/src/main/java/org/jd/gui/api/model/Type.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.api.model;\n\nimport javax.swing.*;\nimport java.util.Collection;\n\npublic interface Type {\n    int FLAG_PUBLIC = 1;\n    int FLAG_PRIVATE = 2;\n    int FLAG_PROTECTED = 4;\n    int FLAG_STATIC = 8;\n    int FLAG_FINAL = 16;\n    int FLAG_VARARGS = 128;\n    int FLAG_INTERFACE = 512;\n    int FLAG_ABSTRACT = 1024;\n    int FLAG_ANNOTATION = 8192;\n    int FLAG_ENUM = 16384;\n\n    int getFlags();\n\n    String getName();\n\n    String getSuperName();\n\n    String getOuterName();\n\n    String getDisplayTypeName();\n\n    String getDisplayInnerTypeName();\n\n    String getDisplayPackageName();\n\n    Icon getIcon();\n\n    Collection<Type> getInnerTypes();\n\n    Collection<Field> getFields();\n\n    Collection<Method> getMethods();\n\n    interface Field {\n        int getFlags();\n\n        String getName();\n\n        String getDescriptor();\n\n        String getDisplayName();\n\n        Icon getIcon();\n    }\n\n    interface Method {\n        int getFlags();\n\n        String getName();\n\n        String getDescriptor();\n\n        String getDisplayName();\n\n        Icon getIcon();\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/spi/ContainerFactory.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.spi;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\n\nimport java.nio.file.Path;\n\npublic interface ContainerFactory {\n    String getType();\n\n    boolean accept(API api, Path rootPath);\n\n    Container make(API api, Container.Entry parentEntry, Path rootPath);\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/spi/ContextualActionsFactory.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.spi;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\n\nimport javax.swing.*;\nimport java.util.Collection;\n\npublic interface ContextualActionsFactory {\n    String GROUP_NAME = \"GroupNameKey\";\n\n    /**\n     * Build a collection of actions for 'entry' and 'fragment', grouped by GROUP_NAME and sorted by NAME. Null values\n     * are added for separators.\n     *\n     * @param fragment @see jd.gui.api.feature.UriOpenable\n     * @return a collection of actions\n     */\n    Collection<Action> make(API api, Container.Entry entry, String fragment);\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/spi/FileLoader.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.spi;\n\nimport org.jd.gui.api.API;\n\nimport java.io.File;\n\npublic interface FileLoader {\n\tString[] getExtensions();\n\t\n\tString getDescription();\n\t\n\tboolean accept(API api, File file);\n\t\n\tboolean load(API api, File file);\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/spi/Indexer.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.spi;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\n\nimport java.util.regex.Pattern;\n\npublic interface Indexer {\n    String[] getSelectors();\n\n    Pattern getPathPattern();\n\n    void index(API api, Container.Entry entry, Indexes indexes);\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/spi/PanelFactory.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.spi;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\n\nimport javax.swing.*;\n\npublic interface PanelFactory {\n\tString[] getTypes();\n\t\n\t<T extends JComponent & UriGettable> T make(API api, Container container);\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/spi/PasteHandler.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.spi;\n\nimport org.jd.gui.api.API;\n\npublic interface PasteHandler {\n    boolean accept(Object obj);\n\n    void paste(API api, Object obj);\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/spi/PreferencesPanel.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.spi;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.util.Map;\n\npublic interface PreferencesPanel {\n    String getPreferencesGroupTitle();\n\n    String getPreferencesPanelTitle();\n\n    JComponent getPanel();\n\n    void init(Color errorBackgroundColor);\n\n    boolean isActivated();\n\n    void loadPreferences(Map<String, String> preferences);\n\n    void savePreferences(Map<String, String> preferences);\n\n    boolean arePreferencesValid();\n\n    void addPreferencesChangeListener(PreferencesPanelChangeListener listener);\n\n    interface PreferencesPanelChangeListener {\n        void preferencesPanelChanged(PreferencesPanel source);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/spi/SourceLoader.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.spi;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\n\nimport java.io.File;\n\npublic interface SourceLoader {\n    String getSource(API api, Container.Entry entry);\n\n    String loadSource(API api, Container.Entry entry);\n\n    File loadSourceFile(API api, Container.Entry entry);\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/spi/SourceSaver.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.spi;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\n\nimport java.nio.file.Path;\nimport java.util.regex.Pattern;\n\npublic interface SourceSaver {\n    String[] getSelectors();\n\n    Pattern getPathPattern();\n\n    String getSourcePath(Container.Entry entry);\n\n    int getFileCount(API api, Container.Entry entry);\n\n    /**\n     * Check parent path, build source file name, create NIO path and save the content.\n     */\n    void save(API api, Controller controller, Listener listener, Path rootPath, Container.Entry entry);\n\n    /**\n     * Save content:\n     * <ul>\n     * <li>For file, save the source content.</li>\n     * <li>For directory, call 'save' for each children.</li>\n     * </ul>\n     */\n    void saveContent(API api, Controller controller, Listener listener, Path rootPath, Path path, Container.Entry entry);\n\n    interface Controller {\n        boolean isCancelled();\n    }\n\n    interface Listener {\n        void pathSaved(Path path);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/spi/TreeNodeFactory.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.spi;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\n\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.util.regex.Pattern;\n\npublic interface TreeNodeFactory {\n    String[] getSelectors();\n\n    Pattern getPathPattern();\n\n    <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry);\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/spi/TypeFactory.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.spi;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Type;\n\nimport java.util.Collection;\nimport java.util.regex.Pattern;\n\npublic interface TypeFactory {\n\tString[] getSelectors();\n\n    Pattern getPathPattern();\n\n\t/**\n\t * @return all root types contains in 'entry'\n\t */\n\tCollection<Type> make(API api, Container.Entry entry);\n\n\t/**\n     * @param fragment @see jd.gui.api.feature.UriOpenable\n\t * @return if 'fragment' is null, return the main type in 'entry',\n\t *         otherwise, return the type or sub-type matching with 'fragment'\n\t */\n\tType make(API api, Container.Entry entry, String fragment);\n}\n"
  },
  {
    "path": "api/src/main/java/org/jd/gui/spi/UriLoader.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.spi;\n\nimport org.jd.gui.api.API;\n\nimport java.net.URI;\n\npublic interface UriLoader {\n\tString[] getSchemes();\n\t\n\tboolean accept(API api, URI uri);\n\t\n\tboolean load(API api, URI uri);\n}\n"
  },
  {
    "path": "app/build.gradle",
    "content": "apply plugin: 'java'\n\ndependencies {\n    provided 'com.yuvimasory:orange-extensions:1.3.0'   // OSX support\n    compile project(':api')\n    runtime project(':services')\n}\n\nversion = parent.version\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/App.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui;\n\nimport org.jd.gui.controller.MainController;\nimport org.jd.gui.model.configuration.Configuration;\nimport org.jd.gui.service.configuration.ConfigurationPersister;\nimport org.jd.gui.service.configuration.ConfigurationPersisterService;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.util.net.InterProcessCommunicationUtil;\n\nimport javax.swing.*;\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\n\npublic class App {\n    protected static final String SINGLE_INSTANCE = \"UIMainWindowPreferencesProvider.singleInstance\";\n\n    protected static MainController controller;\n\n    public static void main(String[] args) {\n\t\tif (checkHelpFlag(args)) {\n\t\t\tJOptionPane.showMessageDialog(null, \"Usage: jd-gui [option] [input-file] ...\\n\\nOption:\\n -h Show this help message and exit\", Constants.APP_NAME, JOptionPane.INFORMATION_MESSAGE);\n\t\t} else {\n            // Load preferences\n            ConfigurationPersister persister = ConfigurationPersisterService.getInstance().get();\n            Configuration configuration = persister.load();\n            Runtime.getRuntime().addShutdownHook(new Thread(() -> persister.save(configuration)));\n\n            if (\"true\".equals(configuration.getPreferences().get(SINGLE_INSTANCE))) {\n                InterProcessCommunicationUtil ipc = new InterProcessCommunicationUtil();\n                try {\n                    ipc.listen(receivedArgs -> controller.openFiles(newList(receivedArgs)));\n                } catch (Exception notTheFirstInstanceException) {\n                    // Send args to main windows and exit\n                    ipc.send(args);\n                    System.exit(0);\n                }\n            }\n\n            // Create SwingBuilder, set look and feel\n            try {\n                UIManager.setLookAndFeel(configuration.getLookAndFeel());\n            } catch (Exception e) {\n                configuration.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());\n                try {\n                    UIManager.setLookAndFeel(configuration.getLookAndFeel());\n                } catch (Exception ee) {\n                    assert ExceptionUtil.printStackTrace(ee);\n                }\n           }\n\n            // Create main controller and show main frame\n            controller = new MainController(configuration);\n            controller.show(newList(args));\n\t\t}\n\t}\n\n    protected static boolean checkHelpFlag(String[] args) {\n        if (args != null) {\n            for (String arg : args) {\n                if (\"-h\".equals(arg)) {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    protected static List<File> newList(String[] paths) {\n        if (paths == null) {\n            return Collections.emptyList();\n        } else {\n            ArrayList<File> files = new ArrayList<>(paths.length);\n            for (String path : paths) {\n                files.add(new File(path));\n            }\n            return files;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/Constants.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui;\n\npublic class Constants {\n\tpublic static final String APP_NAME = \"JD-GUI\";\n\n\tpublic static final int DEFAULT_WIDTH = 600;\n\tpublic static final int DEFAULT_HEIGHT = 400;\n\n\tpublic static final int MINIMAL_WIDTH = 500;\n\tpublic static final int MINIMAL_HEIGHT = 160;\n\n\tpublic static final String CONFIG_FILENAME = \"jd-gui.cfg\";\n\n\tpublic static final int MAX_RECENT_FILES = 10;\n\tpublic static final int RECENT_FILE_MAX_LENGTH = 200;\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/OsxApp.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui;\n\nimport com.apple.eawt.Application;\n\npublic class OsxApp extends App {\n\n    @SuppressWarnings(\"unchecked\")\n    public static void main(String[] args) {\n        // Create an instance of the mac OSX Application class\n        Application application = Application.getApplication();\n\n        App.main(args);\n\n        // Add an handle invoked when the application is asked to open a list of files\n        application.setOpenFileHandler(e -> controller.openFiles(e.getFiles()));\n\n        // Add an handle invoked when the application is asked to quit\n        application.setQuitHandler((e, r) -> System.exit(0));\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/controller/AboutController.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.controller;\n\nimport org.jd.gui.view.AboutView;\n\nimport javax.swing.*;\n\npublic class AboutController {\n    protected AboutView aboutView;\n\n    public AboutController(JFrame mainFrame) {\n        // Create UI\n        aboutView = new AboutView(mainFrame);\n    }\n\n    public void show() {\n        // Show\n        aboutView.show();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/controller/GoToController.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.controller;\n\nimport org.jd.gui.api.feature.LineNumberNavigable;\nimport org.jd.gui.model.configuration.Configuration;\nimport org.jd.gui.view.GoToView;\n\nimport javax.swing.*;\nimport java.util.function.IntConsumer;\n\npublic class GoToController {\n    protected GoToView goToView;\n\n    public GoToController(Configuration configuration, JFrame mainFrame) {\n        // Create UI\n        goToView = new GoToView(configuration, mainFrame);\n    }\n\n    public void show(LineNumberNavigable navigator, IntConsumer okCallback) {\n        // Show\n        goToView.show(navigator, okCallback);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/controller/MainController.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.controller;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.*;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.model.configuration.Configuration;\nimport org.jd.gui.model.history.History;\nimport org.jd.gui.service.actions.ContextualActionsFactoryService;\nimport org.jd.gui.service.container.ContainerFactoryService;\nimport org.jd.gui.service.fileloader.FileLoaderService;\nimport org.jd.gui.service.indexer.IndexerService;\nimport org.jd.gui.service.mainpanel.PanelFactoryService;\nimport org.jd.gui.service.pastehandler.PasteHandlerService;\nimport org.jd.gui.service.platform.PlatformService;\nimport org.jd.gui.service.preferencespanel.PreferencesPanelService;\nimport org.jd.gui.service.sourceloader.SourceLoaderService;\nimport org.jd.gui.service.sourcesaver.SourceSaverService;\nimport org.jd.gui.service.treenode.TreeNodeFactoryService;\nimport org.jd.gui.service.type.TypeFactoryService;\nimport org.jd.gui.service.uriloader.UriLoaderService;\nimport org.jd.gui.spi.*;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.util.net.UriUtil;\nimport org.jd.gui.util.swing.SwingUtil;\nimport org.jd.gui.view.MainView;\n\nimport javax.swing.*;\nimport javax.swing.filechooser.FileNameExtensionFilter;\nimport javax.swing.filechooser.FileSystemView;\nimport java.awt.*;\nimport java.awt.datatransfer.DataFlavor;\nimport java.awt.datatransfer.Transferable;\nimport java.awt.event.ComponentAdapter;\nimport java.awt.event.ComponentEvent;\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.net.URI;\nimport java.nio.file.Path;\nimport java.util.*;\nimport java.util.List;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.TimeUnit;\n\npublic class MainController implements API {\n    protected Configuration configuration;\n    protected MainView mainView;\n\n    protected GoToController goToController;\n    protected OpenTypeController openTypeController;\n    protected OpenTypeHierarchyController openTypeHierarchyController;\n    protected PreferencesController preferencesController;\n    protected SearchInConstantPoolsController searchInConstantPoolsController;\n    protected SaveAllSourcesController saveAllSourcesController;\n    protected SelectLocationController selectLocationController;\n    protected AboutController aboutController;\n    protected SourceLoaderService sourceLoaderService;\n\n    protected History history = new History();\n    protected JComponent currentPage = null;\n    protected ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);\n    protected ArrayList<IndexesChangeListener> containerChangeListeners = new ArrayList<>();\n\n    @SuppressWarnings(\"unchecked\")\n    public MainController(Configuration configuration) {\n        this.configuration = configuration;\n\n        SwingUtil.invokeLater(() -> {\n            if (PlatformService.getInstance().isLinux()) {\n                // Fix for GTKLookAndFeel\n                SwingUtil.installGtkPopupBugWorkaround();\n            }\n\n            // Create main frame\n            mainView = new MainView(\n                configuration, this, history,\n                e -> onOpen(),\n                e -> onClose(),\n                e -> onSaveSource(),\n                e -> onSaveAllSources(),\n                e -> System.exit(0),\n                e -> onCopy(),\n                e -> onPaste(),\n                e -> onSelectAll(),\n                e -> onFind(),\n                e -> onFindPrevious(),\n                e -> onFindNext(),\n                e -> onFindCriteriaChanged(),\n                () -> onFindCriteriaChanged(),\n                e -> onOpenType(),\n                e -> onOpenTypeHierarchy(),\n                e -> onGoTo(),\n                e -> openURI(history.backward()),\n                e -> openURI(history.forward()),\n                e -> onSearch(),\n                e -> onJdWebSite(),\n                e -> onJdGuiIssues(),\n                e -> onJdCoreIssues(),\n                e -> onPreferences(),\n                e -> onAbout(),\n                () -> panelClosed(),\n                page -> onCurrentPageChanged((JComponent)page),\n                file -> openFile((File)file));\n        });\n\t}\n\n\t// --- Show GUI --- //\n    @SuppressWarnings(\"unchecked\")\n\tpublic void show(List<File> files) {\n        SwingUtil.invokeLater(() -> {\n            // Show main frame\n            mainView.show(configuration.getMainWindowLocation(), configuration.getMainWindowSize(), configuration.isMainWindowMaximize());\n            if (!files.isEmpty()) {\n                openFiles(files);\n            }\n        });\n\n        // Background initializations\n        executor.schedule(() -> {\n            // Background service initialization\n            UriLoaderService.getInstance();\n            FileLoaderService.getInstance();\n            ContainerFactoryService.getInstance();\n            IndexerService.getInstance();\n            TreeNodeFactoryService.getInstance();\n            TypeFactoryService.getInstance();\n\n            SwingUtil.invokeLater(() -> {\n                // Populate recent files menu\n                mainView.updateRecentFilesMenu(configuration.getRecentFiles());\n                // Background controller creation\n                JFrame mainFrame = mainView.getMainFrame();\n                saveAllSourcesController = new SaveAllSourcesController(MainController.this, mainFrame);\n                containerChangeListeners.add(openTypeController = new OpenTypeController(MainController.this, executor, mainFrame));\n                containerChangeListeners.add(openTypeHierarchyController = new OpenTypeHierarchyController(MainController.this, executor, mainFrame));\n                goToController = new GoToController(configuration, mainFrame);\n                containerChangeListeners.add(searchInConstantPoolsController = new SearchInConstantPoolsController(MainController.this, executor, mainFrame));\n                preferencesController = new PreferencesController(configuration, mainFrame, PreferencesPanelService.getInstance().getProviders());\n                selectLocationController = new SelectLocationController(MainController.this, mainFrame);\n                aboutController = new AboutController(mainFrame);\n                sourceLoaderService = new SourceLoaderService();\n                // Add listeners\n                mainFrame.addComponentListener(new MainFrameListener(configuration));\n                // Set drop files transfer handler\n                mainFrame.setTransferHandler(new FilesTransferHandler());\n                // Background class loading\n                new JFileChooser().addChoosableFileFilter(new FileNameExtensionFilter(\"\", \"dummy\"));\n                FileSystemView.getFileSystemView().isFileSystemRoot(new File(\"dummy\"));\n                new JLayer();\n            });\n        }, 400, TimeUnit.MILLISECONDS);\n\n        PasteHandlerService.getInstance();\n        PreferencesPanelService.getInstance();\n        ContextualActionsFactoryService.getInstance();\n        SourceSaverService.getInstance();\n    }\n\n\t// --- Actions --- //\n    protected void onOpen() {\n        Map<String, FileLoader> loaders = FileLoaderService.getInstance().getMapProviders();\n        StringBuilder sb = new StringBuilder();\n        ArrayList<String> extensions = new ArrayList<>(loaders.keySet());\n\n        extensions.sort(null);\n\n        for (String extension : extensions) {\n            sb.append(\"*.\").append(extension).append(\", \");\n        }\n\n        sb.setLength(sb.length()-2);\n\n        String description = sb.toString();\n        String[] array = extensions.toArray(new String[0]);\n        JFileChooser chooser = new JFileChooser();\n\n        chooser.removeChoosableFileFilter(chooser.getFileFilter());\n        chooser.addChoosableFileFilter(new FileNameExtensionFilter(\"All files (\" + description + \")\", array));\n\n        for (String extension : extensions) {\n            FileLoader loader = loaders.get(extension);\n            chooser.addChoosableFileFilter(new FileNameExtensionFilter(loader.getDescription(), loader.getExtensions()));\n        }\n\n        chooser.setCurrentDirectory(configuration.getRecentLoadDirectory());\n\n        if (chooser.showOpenDialog(mainView.getMainFrame()) == JFileChooser.APPROVE_OPTION) {\n            configuration.setRecentLoadDirectory(chooser.getCurrentDirectory());\n            openFile(chooser.getSelectedFile());\n        }\n\t}\n\n    protected void onClose() {\n        mainView.closeCurrentTab();\n    }\n\n    protected void onSaveSource() {\n        if (currentPage instanceof ContentSavable) {\n            JFileChooser chooser = new JFileChooser();\n            JFrame mainFrame = mainView.getMainFrame();\n\n            chooser.setSelectedFile(new File(configuration.getRecentSaveDirectory(), ((ContentSavable)currentPage).getFileName()));\n\n            if (chooser.showSaveDialog(mainFrame) == JFileChooser.APPROVE_OPTION) {\n                File selectedFile = chooser.getSelectedFile();\n\n                configuration.setRecentSaveDirectory(chooser.getCurrentDirectory());\n\n                if (selectedFile.exists()) {\n                    String title = \"Are you sure?\";\n                    String message = \"The file '\" + selectedFile.getAbsolutePath() + \"' already isContainsIn.\\n Do you want to replace the existing file?\";\n\n                    if (JOptionPane.showConfirmDialog(mainFrame, message, title, JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {\n                        save(selectedFile);\n                    }\n                } else {\n                    save(selectedFile);\n                }\n            }\n        }\n    }\n\n    protected void save(File selectedFile) {\n        try (OutputStream os = new FileOutputStream(selectedFile)) {\n            ((ContentSavable)currentPage).save(this, os);\n        } catch (IOException e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n    }\n\n    protected void onSaveAllSources() {\n        if (! saveAllSourcesController.isActivated()) {\n            JComponent currentPanel = mainView.getSelectedMainPanel();\n\n            if (currentPanel instanceof SourcesSavable) {\n                SourcesSavable sourcesSavable = (SourcesSavable)currentPanel;\n                JFileChooser chooser = new JFileChooser();\n                JFrame mainFrame = mainView.getMainFrame();\n\n                chooser.setSelectedFile(new File(configuration.getRecentSaveDirectory(), sourcesSavable.getSourceFileName()));\n\n                if (chooser.showSaveDialog(mainFrame) == JFileChooser.APPROVE_OPTION) {\n                    File selectedFile = chooser.getSelectedFile();\n\n                    configuration.setRecentSaveDirectory(chooser.getCurrentDirectory());\n\n                    if (selectedFile.exists()) {\n                        String title = \"Are you sure?\";\n                        String message = \"The file '\" + selectedFile.getAbsolutePath() + \"' already isContainsIn.\\n Do you want to replace the existing file?\";\n\n                        if (JOptionPane.showConfirmDialog(mainFrame, message, title, JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {\n                            saveAllSourcesController.show(executor, sourcesSavable, selectedFile);\n                        }\n                    } else {\n                        saveAllSourcesController.show(executor, sourcesSavable, selectedFile);\n                    }\n                }\n            }\n        }\n    }\n\n    protected void onCopy() {\n        if (currentPage instanceof ContentCopyable) {\n            ((ContentCopyable)currentPage).copy();\n        }\n    }\n\n    protected void onPaste() {\n        try {\n            Transferable transferable = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null);\n\n            if ((transferable != null) && transferable.isDataFlavorSupported(DataFlavor.stringFlavor)) {\n                Object obj = transferable.getTransferData(DataFlavor.stringFlavor);\n                PasteHandler pasteHandler = PasteHandlerService.getInstance().get(obj);\n\n                if (pasteHandler != null) {\n                    pasteHandler.paste(this, obj);\n                }\n            }\n        } catch (Exception e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n    }\n\n    protected void onSelectAll() {\n        if (currentPage instanceof ContentSelectable) {\n            ((ContentSelectable)currentPage).selectAll();\n        }\n    }\n\n    protected void onFind() {\n        if (currentPage instanceof ContentSearchable) {\n            mainView.showFindPanel();\n        }\n    }\n\n    protected void onFindCriteriaChanged() {\n        if (currentPage instanceof ContentSearchable) {\n            mainView.setFindBackgroundColor(((ContentSearchable)currentPage).highlightText(mainView.getFindText(), mainView.getFindCaseSensitive()));\n        }\n    }\n\n    protected void onFindNext() {\n        if (currentPage instanceof ContentSearchable) {\n            ((ContentSearchable)currentPage).findNext(mainView.getFindText(), mainView.getFindCaseSensitive());\n        }\n    }\n\n    protected void onOpenType() {\n        openTypeController.show(getCollectionOfFutureIndexes(), uri -> openURI(uri));\n    }\n\n    protected void onOpenTypeHierarchy() {\n        if (currentPage instanceof FocusedTypeGettable) {\n            FocusedTypeGettable ftg = (FocusedTypeGettable)currentPage;\n            openTypeHierarchyController.show(getCollectionOfFutureIndexes(), ftg.getEntry(), ftg.getFocusedTypeName(), uri -> openURI(uri));\n        }\n    }\n\n    protected void onGoTo() {\n        if (currentPage instanceof LineNumberNavigable) {\n            LineNumberNavigable lnn = (LineNumberNavigable)currentPage;\n            goToController.show(lnn, lineNumber -> lnn.goToLineNumber(lineNumber));\n        }\n    }\n\n    protected void onSearch() {\n        searchInConstantPoolsController.show(getCollectionOfFutureIndexes(), uri -> openURI(uri));\n    }\n\n    protected void onFindPrevious() {\n        if (currentPage instanceof ContentSearchable) {\n            ContentSearchable cs = (ContentSearchable)currentPage;\n            cs.findPrevious(mainView.getFindText(), mainView.getFindCaseSensitive());\n        }\n    }\n\n    protected void onJdWebSite() {\n        if (Desktop.isDesktopSupported()) {\n            Desktop desktop = Desktop.getDesktop();\n            if (desktop.isSupported(Desktop.Action.BROWSE)) {\n                try {\n                    desktop.browse(URI.create(\"http://java-decompiler.github.io\"));\n                } catch (IOException e) {\n                    assert ExceptionUtil.printStackTrace(e);\n                }\n            }\n        }\n    }\n\n    protected void onJdGuiIssues() {\n        if (Desktop.isDesktopSupported()) {\n            Desktop desktop = Desktop.getDesktop();\n            if (desktop.isSupported(Desktop.Action.BROWSE)) {\n                try {\n                    desktop.browse(URI.create(\"https://github.com/java-decompiler/jd-gui/issues\"));\n                } catch (IOException e) {\n                    assert ExceptionUtil.printStackTrace(e);\n                }\n            }\n        }\n    }\n\n    protected void onJdCoreIssues() {\n        if (Desktop.isDesktopSupported()) {\n            Desktop desktop = Desktop.getDesktop();\n            if (desktop.isSupported(Desktop.Action.BROWSE)) {\n                try {\n                    desktop.browse(URI.create(\"https://github.com/java-decompiler/jd-core/issues\"));\n                } catch (IOException e) {\n                    assert ExceptionUtil.printStackTrace(e);\n                }\n            }\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected void onPreferences() {\n        preferencesController.show(() -> {\n            checkPreferencesChange(currentPage);\n            mainView.preferencesChanged(getPreferences());\n        });\n    }\n\n    protected void onAbout() {\n        aboutController.show();\n    }\n\n    protected void onCurrentPageChanged(JComponent page) {\n        currentPage = page;\n        checkPreferencesChange(page);\n        checkIndexesChange(page);\n    }\n\n    protected void checkPreferencesChange(JComponent page) {\n        if (page instanceof PreferencesChangeListener) {\n            Map<String, String> preferences = configuration.getPreferences();\n            Integer currentHashcode = Integer.valueOf(preferences.hashCode());\n            Integer lastHashcode = (Integer)page.getClientProperty(\"preferences-hashCode\");\n\n            if (!currentHashcode.equals(lastHashcode)) {\n                ((PreferencesChangeListener)page).preferencesChanged(preferences);\n                page.putClientProperty(\"preferences-hashCode\", currentHashcode);\n            }\n        }\n    }\n\n    protected void checkIndexesChange(JComponent page) {\n        if (page instanceof IndexesChangeListener) {\n            Collection<Future<Indexes>> collectionOfFutureIndexes = getCollectionOfFutureIndexes();\n            Integer currentHashcode = Integer.valueOf(collectionOfFutureIndexes.hashCode());\n            Integer lastHashcode = (Integer)page.getClientProperty(\"collectionOfFutureIndexes-hashCode\");\n\n            if (!currentHashcode.equals(lastHashcode)) {\n                ((IndexesChangeListener)page).indexesChanged(collectionOfFutureIndexes);\n                page.putClientProperty(\"collectionOfFutureIndexes-hashCode\", currentHashcode);\n            }\n        }\n    }\n\n    // --- Operations --- //\n    public void openFile(File file) {\n        openFiles(Collections.singletonList(file));\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public void openFiles(List<File> files) {\n        ArrayList<String> errors = new ArrayList<>();\n\n        for (File file : files) {\n            // Check input file\n            if (file.exists()) {\n                FileLoader loader = getFileLoader(file);\n                if ((loader != null) && !loader.accept(this, file)) {\n                    errors.add(\"Invalid input fileloader: '\" + file.getAbsolutePath() + \"'\");\n                }\n            } else {\n                errors.add(\"File not found: '\" + file.getAbsolutePath() + \"'\");\n            }\n        }\n\n        if (errors.isEmpty()) {\n            for (File file : files) {\n                if (openURI(file.toURI())) {\n                    configuration.addRecentFile(file);\n                    mainView.updateRecentFilesMenu(configuration.getRecentFiles());\n                }\n            }\n        } else {\n            StringBuilder messages = new StringBuilder();\n            int index = 0;\n\n            for (String error : errors) {\n                if (index > 0) {\n                    messages.append('\\n');\n                }\n                if (index >= 20) {\n                    messages.append(\"...\");\n                    break;\n                }\n                messages.append(error);\n                index++;\n            }\n\n            JOptionPane.showMessageDialog(mainView.getMainFrame(), messages.toString(), \"Error\", JOptionPane.ERROR_MESSAGE);\n        }\n    }\n\n    // --- Drop files transfer handler --- //\n    protected class FilesTransferHandler extends TransferHandler {\n        @Override\n        public boolean canImport(TransferHandler.TransferSupport info) {\n            return info.isDataFlavorSupported(DataFlavor.javaFileListFlavor);\n        }\n\n        @Override\n        @SuppressWarnings(\"unchecked\")\n        public boolean importData(TransferHandler.TransferSupport info) {\n            if (info.isDrop() && info.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {\n                try {\n                    openFiles((List<File>)info.getTransferable().getTransferData(DataFlavor.javaFileListFlavor));\n                    return true;\n                } catch (Exception e) {\n                    assert ExceptionUtil.printStackTrace(e);\n                }\n            }\n            return false;\n        }\n    }\n\n    // --- ComponentListener --- //\n    protected class MainFrameListener extends ComponentAdapter {\n        protected Configuration configuration;\n\n        public MainFrameListener(Configuration configuration) {\n            this.configuration = configuration;\n        }\n\n        @Override\n        public void componentMoved(ComponentEvent e) {\n            JFrame mainFrame = mainView.getMainFrame();\n\n            if ((mainFrame.getExtendedState() & Frame.MAXIMIZED_BOTH) == Frame.MAXIMIZED_BOTH) {\n                configuration.setMainWindowMaximize(true);\n            } else {\n                configuration.setMainWindowLocation(mainFrame.getLocation());\n                configuration.setMainWindowMaximize(false);\n            }\n        }\n\n        @Override\n        public void componentResized(ComponentEvent e) {\n            JFrame mainFrame = mainView.getMainFrame();\n\n            if ((mainFrame.getExtendedState() & Frame.MAXIMIZED_BOTH) == Frame.MAXIMIZED_BOTH) {\n                configuration.setMainWindowMaximize(true);\n            } else {\n                configuration.setMainWindowSize(mainFrame.getSize());\n                configuration.setMainWindowMaximize(false);\n            }\n        }\n    }\n\n    protected void panelClosed() {\n        SwingUtil.invokeLater(() -> {\n            // Fire 'indexesChanged' event\n            Collection<Future<Indexes>> collectionOfFutureIndexes = getCollectionOfFutureIndexes();\n            for (IndexesChangeListener listener : containerChangeListeners) {\n                listener.indexesChanged(collectionOfFutureIndexes);\n            }\n            if (currentPage instanceof IndexesChangeListener) {\n                ((IndexesChangeListener)currentPage).indexesChanged(collectionOfFutureIndexes);\n            }\n        });\n    }\n\n    // --- API --- //\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public boolean openURI(URI uri) {\n        if (uri != null) {\n            boolean success = mainView.openUri(uri);\n\n            if (success == false) {\n                UriLoader uriLoader = getUriLoader(uri);\n                if (uriLoader != null) {\n                    success = uriLoader.load(this, uri);\n                }\n            }\n\n            if (success) {\n                addURI(uri);\n            }\n\n            return success;\n        }\n\n        return false;\n    }\n\n    @Override\n    public boolean openURI(int x, int y, Collection<Container.Entry> entries, String query, String fragment) {\n        if (entries != null) {\n            if (entries.size() == 1) {\n                // Open the single entry uri\n                Container.Entry entry = entries.iterator().next();\n                return openURI(UriUtil.createURI(this, getCollectionOfFutureIndexes(), entry, query, fragment));\n            } else {\n                // Multiple entries -> Open a \"Select location\" popup\n                Collection<Future<Indexes>> collectionOfFutureIndexes = getCollectionOfFutureIndexes();\n                selectLocationController.show(\n                    new Point(x+(16+2), y+2),\n                    entries,\n                    entry -> openURI(UriUtil.createURI(this, collectionOfFutureIndexes, entry, query, fragment)), // entry selected closure\n                    () -> {});                                                                                    // popup close closure\n                return true;\n            }\n        }\n\n        return false;\n    }\n\n    @Override\n    public void addURI(URI uri) {\n        history.add(uri);\n        SwingUtil.invokeLater(() -> {\n            mainView.updateHistoryActions();\n        });\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends JComponent & UriGettable> void addPanel(String title, Icon icon, String tip, T component) {\n        mainView.addMainPanel(title, icon, tip, component);\n\n        if (component instanceof ContentIndexable) {\n            Future<Indexes> futureIndexes = executor.submit(() -> {\n                Indexes indexes = ((ContentIndexable)component).index(this);\n\n                SwingUtil.invokeLater(() -> {\n                    // Fire 'indexesChanged' event\n                    Collection<Future<Indexes>> collectionOfFutureIndexes = getCollectionOfFutureIndexes();\n                    for (IndexesChangeListener listener : containerChangeListeners) {\n                        listener.indexesChanged(collectionOfFutureIndexes);\n                    }\n                    if (currentPage instanceof IndexesChangeListener) {\n                        ((IndexesChangeListener) currentPage).indexesChanged(collectionOfFutureIndexes);\n                    }\n                });\n\n                return indexes;\n            });\n\n            component.putClientProperty(\"indexes\", futureIndexes);\n        }\n    }\n\n    @Override public Collection<Action> getContextualActions(Container.Entry entry, String fragment) { return ContextualActionsFactoryService.getInstance().get(this, entry, fragment); }\n\n    @Override public FileLoader getFileLoader(File file) { return FileLoaderService.getInstance().get(this, file); }\n\n    @Override public UriLoader getUriLoader(URI uri) { return UriLoaderService.getInstance().get(this, uri); }\n\n    @Override public PanelFactory getMainPanelFactory(Container container) { return PanelFactoryService.getInstance().get(container); }\n\n    @Override public ContainerFactory getContainerFactory(Path rootPath) { return ContainerFactoryService.getInstance().get(this, rootPath); }\n\n    @Override public TreeNodeFactory getTreeNodeFactory(Container.Entry entry) { return TreeNodeFactoryService.getInstance().get(entry); }\n\n    @Override public TypeFactory getTypeFactory(Container.Entry entry) { return TypeFactoryService.getInstance().get(entry); }\n\n    @Override public Indexer getIndexer(Container.Entry entry) { return IndexerService.getInstance().get(entry); }\n\n    @Override public SourceSaver getSourceSaver(Container.Entry entry) { return SourceSaverService.getInstance().get(entry); }\n\n    @Override public Map<String, String> getPreferences() { return configuration.getPreferences(); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public Collection<Future<Indexes>> getCollectionOfFutureIndexes() {\n        List<JComponent> mainPanels = mainView.getMainPanels();\n        ArrayList<Future<Indexes>> list = new ArrayList<Future<Indexes>>(mainPanels.size()) {\n            @Override\n            public int hashCode() {\n                int hashCode = 1;\n\n                try {\n                    for (Future<Indexes> futureIndexes : this) {\n                        hashCode *= 31;\n\n                        if (futureIndexes.isDone()) {\n                            hashCode += futureIndexes.get().hashCode();\n                        }\n                    }\n                } catch (Exception e) {\n                    assert ExceptionUtil.printStackTrace(e);\n                }\n\n                return hashCode;\n            }\n        };\n\n        for (JComponent panel : mainPanels) {\n            Future<Indexes> futureIndexes = (Future<Indexes>)panel.getClientProperty(\"indexes\");\n            if (futureIndexes != null) {\n                list.add(futureIndexes);\n            }\n        }\n\n        return list;\n    }\n\n    @Override\n    public String getSource(Container.Entry entry) {\n        return sourceLoaderService.getSource(this, entry);\n    }\n\n    @Override\n    public void loadSource(Container.Entry entry, LoadSourceListener listener) {\n        executor.execute(() -> {\n            String source = sourceLoaderService.loadSource(this, entry);\n\n            if ((source != null) && !source.isEmpty()) {\n                listener.sourceLoaded(source);\n            }\n        });\n    }\n\n    @Override\n    public File loadSourceFile(Container.Entry entry) {\n        return sourceLoaderService.getSourceFile(this, entry);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/controller/OpenTypeController.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.controller;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.IndexesChangeListener;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.util.net.UriUtil;\nimport org.jd.gui.view.OpenTypeView;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.net.URI;\nimport java.util.*;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.function.Consumer;\nimport java.util.regex.Pattern;\n\npublic class OpenTypeController implements IndexesChangeListener {\n    protected static final int CACHE_MAX_ENTRIES = 5*20;\n\n    protected API api;\n    protected ScheduledExecutorService executor;\n    protected Collection<Future<Indexes>> collectionOfFutureIndexes;\n    protected Consumer<URI> openCallback;\n\n    protected JFrame mainFrame;\n    protected OpenTypeView openTypeView;\n    protected SelectLocationController selectLocationController;\n\n    protected long indexesHashCode = 0L;\n    protected Map<String, Map<String, Collection>> cache;\n\n    public OpenTypeController(API api, ScheduledExecutorService executor, JFrame mainFrame) {\n        this.api = api;\n        this.executor = executor;\n        this.mainFrame = mainFrame;\n        // Create UI\n        openTypeView = new OpenTypeView(api, mainFrame, this::updateList, this::onTypeSelected);\n        selectLocationController = new SelectLocationController(api, mainFrame);\n        // Create result cache\n        cache = new LinkedHashMap<String, Map<String, Collection>>(CACHE_MAX_ENTRIES*3/2, 0.7f, true) {\n            @Override\n            protected boolean removeEldestEntry(Map.Entry<String, Map<String, Collection>> eldest) {\n                return size() > CACHE_MAX_ENTRIES;\n            }\n        };\n    }\n\n    public void show(Collection<Future<Indexes>> collectionOfFutureIndexes, Consumer<URI> openCallback) {\n        // Init attributes\n        this.collectionOfFutureIndexes = collectionOfFutureIndexes;\n        this.openCallback = openCallback;\n        // Refresh view\n        long hashCode = collectionOfFutureIndexes.hashCode();\n        if (hashCode != indexesHashCode) {\n            // List of indexes has changed -> Refresh result list\n            updateList(openTypeView.getPattern());\n            indexesHashCode = hashCode;\n        }\n        // Show\n        openTypeView.show();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected void updateList(String pattern) {\n        int patternLength = pattern.length();\n\n        if (patternLength == 0) {\n            // Display\n            openTypeView.updateList(Collections.emptyMap());\n        } else {\n            executor.execute(() -> {\n                // Waiting the end of indexation...\n                openTypeView.showWaitCursor();\n\n                Pattern regExpPattern = createRegExpPattern(pattern);\n                Map<String, Collection<Container.Entry>> result = new HashMap<>();\n\n                try {\n                    for (Future<Indexes> futureIndexes : collectionOfFutureIndexes) {\n                        if (futureIndexes.isDone()) {\n                            Indexes indexes = futureIndexes.get();\n                            String key = String.valueOf(indexes.hashCode()) + \"***\" + pattern;\n                            Map<String, Collection> matchingEntries = cache.get(key);\n\n                            if (matchingEntries != null) {\n                                // Merge 'result' and 'matchingEntries'\n                                for (Map.Entry<String, Collection> mapEntry : matchingEntries.entrySet()) {\n                                    Collection<Container.Entry> collection = result.get(mapEntry.getKey());\n                                    if (collection == null) {\n                                        result.put(mapEntry.getKey(), collection = new HashSet<>());\n                                    }\n                                    collection.addAll(mapEntry.getValue());\n                                }\n                            } else {\n                                // Waiting the end of indexation...\n                                Map<String, Collection> index = indexes.getIndex(\"typeDeclarations\");\n\n                                if ((index != null) && !index.isEmpty()) {\n                                    matchingEntries = new HashMap<>();\n\n                                    // Filter\n                                    if (patternLength == 1) {\n                                        match(pattern.charAt(0), index, matchingEntries);\n                                    } else {\n                                        String lastKey = key.substring(0, patternLength - 1);\n                                        Map<String, Collection> lastResult = cache.get(lastKey);\n\n                                        if (lastResult != null) {\n                                            match(regExpPattern, lastResult, matchingEntries);\n                                        } else {\n                                            match(regExpPattern, index, matchingEntries);\n                                        }\n                                    }\n\n                                    // Store 'matchingEntries'\n                                    cache.put(key, matchingEntries);\n\n                                    // Merge 'result' and 'matchingEntries'\n                                    for (Map.Entry<String, Collection> mapEntry : matchingEntries.entrySet()) {\n                                        Collection<Container.Entry> collection = result.get(mapEntry.getKey());\n                                        if (collection == null) {\n                                            result.put(mapEntry.getKey(), collection = new HashSet<>());\n                                        }\n                                        collection.addAll(mapEntry.getValue());\n                                    }\n                                }\n                            }\n                        }\n                    }\n                } catch (Exception e) {\n                    assert ExceptionUtil.printStackTrace(e);\n                }\n\n                SwingUtilities.invokeLater(() -> {\n                    openTypeView.hideWaitCursor();\n                    // Display\n                    openTypeView.updateList(result);\n                });\n            });\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected static void match(char c, Map<String, Collection> index, Map<String, Collection> result) {\n        // Filter\n        if (Character.isLowerCase(c)) {\n            char upperCase = Character.toUpperCase(c);\n\n            for (Map.Entry<String, Collection> mapEntry : index.entrySet()) {\n                String typeName = mapEntry.getKey();\n                Collection<Container.Entry> entries = mapEntry.getValue();\n                // Search last package separator\n                int lastPackageSeparatorIndex = typeName.lastIndexOf('/') + 1;\n                int lastTypeNameSeparatorIndex = typeName.lastIndexOf('$') + 1;\n                int lastIndex = Math.max(lastPackageSeparatorIndex, lastTypeNameSeparatorIndex);\n\n                if (lastIndex < typeName.length()) {\n                    char first = typeName.charAt(lastIndex);\n\n                    if ((first == c) || (first == upperCase)) {\n                        add(result, typeName, entries);\n                    }\n                }\n            }\n        } else {\n            for (Map.Entry<String, Collection> mapEntry : index.entrySet()) {\n                String typeName = mapEntry.getKey();\n                Collection<Container.Entry> entries = mapEntry.getValue();\n                // Search last package separator\n                int lastPackageSeparatorIndex = typeName.lastIndexOf('/') + 1;\n                int lastTypeNameSeparatorIndex = typeName.lastIndexOf('$') + 1;\n                int lastIndex = Math.max(lastPackageSeparatorIndex, lastTypeNameSeparatorIndex);\n\n                if ((lastIndex < typeName.length()) && (typeName.charAt(lastIndex) == c)) {\n                    add(result, typeName, entries);\n                }\n            }\n        }\n    }\n\n    /**\n     * Create a regular expression to match package, type and inner type name.\n     *\n     * Rules:\n     *  '*'        matches 0 ou N characters\n     *  '?'        matches 1 character\n     *  lower case matches insensitive case\n     *  upper case matches upper case\n     */\n    protected static Pattern createRegExpPattern(String pattern) {\n        // Create regular expression\n        int patternLength = pattern.length();\n        StringBuilder sbPattern = new StringBuilder(patternLength * 4);\n\n        for (int i=0; i<patternLength; i++) {\n            char c = pattern.charAt(i);\n\n            if (Character.isUpperCase(c)) {\n                if (i > 1) {\n                    sbPattern.append(\".*\");\n                }\n                sbPattern.append(c);\n            } else if (Character.isLowerCase(c)) {\n                sbPattern.append('[').append(c).append(Character.toUpperCase(c)).append(']');\n            } else if (c == '*') {\n                sbPattern.append(\".*\");\n            } else if (c == '?') {\n                sbPattern.append(\".\");\n            } else {\n                sbPattern.append(c);\n            }\n        }\n\n        sbPattern.append(\".*\");\n\n        return Pattern.compile(sbPattern.toString());\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected static void match(Pattern regExpPattern, Map<String, Collection> index, Map<String, Collection> result) {\n        for (Map.Entry<String, Collection> mapEntry : index.entrySet()) {\n            String typeName = mapEntry.getKey();\n            Collection<Container.Entry> entries = mapEntry.getValue();\n            // Search last package separator\n            int lastPackageSeparatorIndex = typeName.lastIndexOf('/') + 1;\n            int lastTypeNameSeparatorIndex = typeName.lastIndexOf('$') + 1;\n            int lastIndex = Math.max(lastPackageSeparatorIndex, lastTypeNameSeparatorIndex);\n\n            if (regExpPattern.matcher(typeName.substring(lastIndex)).matches()) {\n                add(result, typeName, entries);\n            }\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected static void add(Map<String, Collection> map, String key, Collection value) {\n        Collection<Container.Entry> collection = map.get(key);\n\n        if (collection == null) {\n            map.put(key, collection = new HashSet<>());\n        }\n\n        collection.addAll(value);\n    }\n\n    protected void onTypeSelected(Point leftBottom, Collection<Container.Entry> entries, String typeName) {\n        if (entries.size() == 1) {\n            // Open the single entry uri\n            openCallback.accept(UriUtil.createURI(api, collectionOfFutureIndexes, entries.iterator().next(), null, typeName));\n        } else {\n            // Multiple entries -> Open a \"Select location\" popup\n            selectLocationController.show(\n                new Point(leftBottom.x+(16+2), leftBottom.y+2),\n                entries,\n                (entry) -> openCallback.accept(UriUtil.createURI(api, collectionOfFutureIndexes, entry, null, typeName)), // entry selected callback\n                () -> openTypeView.focus());                                                                              // popup close callback\n        }\n    }\n\n    // --- IndexesChangeListener --- //\n    public void indexesChanged(Collection<Future<Indexes>> collectionOfFutureIndexes) {\n        if (openTypeView.isVisible()) {\n            // Update the list of containers\n            this.collectionOfFutureIndexes = collectionOfFutureIndexes;\n            // And refresh\n            updateList(openTypeView.getPattern());\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/controller/OpenTypeHierarchyController.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.controller;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.IndexesChangeListener;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.util.net.UriUtil;\nimport org.jd.gui.view.OpenTypeHierarchyView;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.net.URI;\nimport java.util.Collection;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.function.Consumer;\n\npublic class OpenTypeHierarchyController implements IndexesChangeListener {\n    protected API api;\n    private ScheduledExecutorService executor;\n\n    protected JFrame mainFrame;\n    protected OpenTypeHierarchyView openTypeHierarchyView;\n    protected SelectLocationController selectLocationController;\n\n    protected Collection<Future<Indexes>> collectionOfFutureIndexes;\n    protected Consumer<URI> openCallback;\n\n    public OpenTypeHierarchyController(API api, ScheduledExecutorService executor, JFrame mainFrame) {\n        this.api = api;\n        this.executor = executor;\n        this.mainFrame = mainFrame;\n        // Create UI\n        openTypeHierarchyView = new OpenTypeHierarchyView(api, mainFrame, this::onTypeSelected);\n        selectLocationController = new SelectLocationController(api, mainFrame);\n    }\n\n    public void show(Collection<Future<Indexes>> collectionOfFutureIndexes, Container.Entry entry, String typeName, Consumer<URI> openCallback) {\n        // Init attributes\n        this.collectionOfFutureIndexes = collectionOfFutureIndexes;\n        this.openCallback = openCallback;\n        executor.execute(() -> {\n            // Waiting the end of indexation...\n            openTypeHierarchyView.showWaitCursor();\n            SwingUtilities.invokeLater(() -> {\n                openTypeHierarchyView.hideWaitCursor();\n                // Show\n                openTypeHierarchyView.show(collectionOfFutureIndexes, entry, typeName);\n            });\n        });\n    }\n\n    protected void onTypeSelected(Point leftBottom, Collection<Container.Entry> entries, String typeName) {\n        if (entries.size() == 1) {\n            // Open the single entry uri\n            openCallback.accept(UriUtil.createURI(api, collectionOfFutureIndexes, entries.iterator().next(), null, typeName));\n        } else {\n            // Multiple entries -> Open a \"Select location\" popup\n            selectLocationController.show(\n                new Point(leftBottom.x+(16+2), leftBottom.y+2),\n                entries,\n                (entry) -> openCallback.accept(UriUtil.createURI(api, collectionOfFutureIndexes, entry, null, typeName)), // entry selected\n                () -> openTypeHierarchyView.focus());                                                               // popup closeClosure\n        }\n    }\n\n    // --- IndexesChangeListener --- //\n    public void indexesChanged(Collection<Future<Indexes>> collectionOfFutureIndexes) {\n        if (openTypeHierarchyView.isVisible()) {\n            // Update the list of containers\n            this.collectionOfFutureIndexes = collectionOfFutureIndexes;\n            // And refresh\n            openTypeHierarchyView.updateTree(collectionOfFutureIndexes);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/controller/PreferencesController.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.controller;\n\nimport org.jd.gui.model.configuration.Configuration;\nimport org.jd.gui.spi.PreferencesPanel;\nimport org.jd.gui.view.PreferencesView;\n\nimport javax.swing.*;\nimport java.util.Collection;\n\npublic class PreferencesController {\n    protected PreferencesView preferencesView;\n\n    public PreferencesController(Configuration configuration, JFrame mainFrame, Collection<PreferencesPanel> panels) {\n        // Create UI\n        preferencesView = new PreferencesView(configuration, mainFrame, panels);\n    }\n\n    public void show(Runnable okCallback) {\n        // Show\n        preferencesView.show(okCallback);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/controller/SaveAllSourcesController.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.controller;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.SourcesSavable;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.view.SaveAllSourcesView;\n\nimport javax.swing.*;\nimport java.io.File;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.Paths;\nimport java.util.concurrent.ScheduledExecutorService;\n\npublic class SaveAllSourcesController implements SourcesSavable.Controller, SourcesSavable.Listener {\n    protected API api;\n    protected SaveAllSourcesView saveAllSourcesView;\n    protected boolean cancel;\n    protected int counter;\n    protected int mask;\n\n    public SaveAllSourcesController(API api, JFrame mainFrame) {\n        this.api = api;\n        // Create UI\n        this.saveAllSourcesView = new SaveAllSourcesView(mainFrame, this::onCanceled);\n    }\n\n    public void show(ScheduledExecutorService executor, SourcesSavable savable, File file) {\n        // Show\n        this.saveAllSourcesView.show(file);\n        // Execute background task\n        executor.execute(() -> {\n            int fileCount = savable.getFileCount();\n\n            saveAllSourcesView.updateProgressBar(0);\n            saveAllSourcesView.setMaxValue(fileCount);\n\n            cancel = false;\n            counter = 0;\n            mask = 2;\n\n            while (fileCount > 64) {\n                fileCount >>= 1;\n                mask <<= 1;\n            }\n\n            mask--;\n\n            try {\n                Path path = Paths.get(file.toURI());\n                Files.deleteIfExists(path);\n\n                try {\n                    savable.save(api, this, this, path);\n                } catch (Exception e) {\n                    assert ExceptionUtil.printStackTrace(e);\n                    saveAllSourcesView.showActionFailedDialog();\n                    cancel = true;\n                }\n\n                if (cancel) {\n                    Files.deleteIfExists(path);\n                }\n            } catch (Throwable t) {\n                assert ExceptionUtil.printStackTrace(t);\n            }\n\n            saveAllSourcesView.hide();\n        });\n    }\n\n    public boolean isActivated() { return saveAllSourcesView.isVisible(); }\n\n    protected void onCanceled() { cancel = true; }\n\n    // --- SourcesSavable.Controller --- //\n    @Override public boolean isCancelled() { return cancel; }\n\n    // --- SourcesSavable.Listener --- //\n    @Override\n    public void pathSaved(Path path) {\n        if (((counter++) & mask) == 0) {\n            saveAllSourcesView.updateProgressBar(counter);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/controller/SearchInConstantPoolsController.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.controller;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.IndexesChangeListener;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.api.model.Type;\nimport org.jd.gui.model.container.DelegatingFilterContainer;\nimport org.jd.gui.service.type.TypeFactoryService;\nimport org.jd.gui.spi.TypeFactory;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.util.function.TriConsumer;\nimport org.jd.gui.view.SearchInConstantPoolsView;\n\nimport javax.swing.*;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.*;\nimport java.util.concurrent.Future;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.function.BiConsumer;\nimport java.util.function.BiFunction;\nimport java.util.function.Consumer;\nimport java.util.regex.Pattern;\n\npublic class SearchInConstantPoolsController implements IndexesChangeListener {\n    protected static final int CACHE_MAX_ENTRIES = 5*20*9;\n\n    protected API api;\n    protected ScheduledExecutorService executor;\n\n    protected JFrame mainFrame;\n    protected SearchInConstantPoolsView searchInConstantPoolsView;\n    protected Map<String, Map<String, Collection>> cache;\n    protected Set<DelegatingFilterContainer> delegatingFilterContainers = new HashSet<>();\n    protected Collection<Future<Indexes>> collectionOfFutureIndexes;\n    protected Consumer<URI> openCallback;\n    protected long indexesHashCode = 0L;\n\n    @SuppressWarnings(\"unchecked\")\n    public SearchInConstantPoolsController(API api, ScheduledExecutorService executor, JFrame mainFrame) {\n        this.api = api;\n        this.executor = executor;\n        this.mainFrame = mainFrame;\n        // Create UI\n        this.searchInConstantPoolsView = new SearchInConstantPoolsView(\n            api, mainFrame,\n            new BiConsumer<String, Integer>() {\n                @Override public void accept(String pattern, Integer flags) { updateTree(pattern, flags); }\n            },\n            new TriConsumer<URI, String, Integer>() {\n                @Override public void accept(URI uri, String pattern, Integer flags) { onTypeSelected(uri, pattern, flags); }\n            }\n        );\n        // Create result cache\n        this.cache = new LinkedHashMap<String, Map<String, Collection>>(CACHE_MAX_ENTRIES*3/2, 0.7f, true) {\n            @Override\n            protected boolean removeEldestEntry(Map.Entry<String, Map<String, Collection>> eldest) {\n                return size() > CACHE_MAX_ENTRIES;\n            }\n        };\n    }\n\n    public void show(Collection<Future<Indexes>> collectionOfFutureIndexes, Consumer<URI> openCallback) {\n        // Init attributes\n        this.collectionOfFutureIndexes = collectionOfFutureIndexes;\n        this.openCallback = openCallback;\n        // Refresh view\n        long hashCode = collectionOfFutureIndexes.hashCode();\n        if (hashCode != indexesHashCode) {\n            // List of indexes has changed\n            updateTree(searchInConstantPoolsView.getPattern(), searchInConstantPoolsView.getFlags());\n            indexesHashCode = hashCode;\n        }\n        // Show\n        searchInConstantPoolsView.show();\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected void updateTree(String pattern, int flags) {\n        delegatingFilterContainers.clear();\n\n        executor.execute(() -> {\n            // Waiting the end of indexation...\n            searchInConstantPoolsView.showWaitCursor();\n\n            int matchingTypeCount = 0;\n            int patternLength = pattern.length();\n\n            if (patternLength > 0) {\n                try {\n                    for (Future<Indexes> futureIndexes : collectionOfFutureIndexes) {\n                        if (futureIndexes.isDone()) {\n                            Indexes indexes = futureIndexes.get();\n                            HashSet<Container.Entry> matchingEntries = new HashSet<>();\n                            // Find matched entries\n                            filter(indexes, pattern, flags, matchingEntries);\n\n                            if (!matchingEntries.isEmpty()) {\n                                // Search root container with first matching entry\n                                Container.Entry parentEntry = matchingEntries.iterator().next();\n                                Container container = null;\n\n                                while (parentEntry.getContainer().getRoot() != null) {\n                                    container = parentEntry.getContainer();\n                                    parentEntry = container.getRoot().getParent();\n                                }\n\n                                // TODO In a future release, display matching strings, types, inner-types, fields and methods, not only matching files\n                                matchingEntries = getOuterEntries(matchingEntries);\n\n                                matchingTypeCount += matchingEntries.size();\n\n                                // Create a filtered container\n                                delegatingFilterContainers.add(new DelegatingFilterContainer(container, matchingEntries));\n                            }\n                        }\n                    }\n                } catch (Exception e) {\n                    assert ExceptionUtil.printStackTrace(e);\n                }\n            }\n\n            final int count = matchingTypeCount;\n\n            searchInConstantPoolsView.hideWaitCursor();\n            searchInConstantPoolsView.updateTree(delegatingFilterContainers, count);\n        });\n    }\n\n    protected HashSet<Container.Entry> getOuterEntries(Set<Container.Entry> matchingEntries) {\n        HashMap<Container.Entry, Container.Entry> innerTypeEntryToOuterTypeEntry = new HashMap<>();\n        HashSet<Container.Entry> matchingOuterEntriesSet = new HashSet<>();\n\n        for (Container.Entry entry : matchingEntries) {\n            TypeFactory typeFactory = TypeFactoryService.getInstance().get(entry);\n\n            if (typeFactory != null) {\n                Type type = typeFactory.make(api, entry, null);\n\n                if ((type != null) && (type.getOuterName() != null)) {\n                    Container.Entry outerTypeEntry = innerTypeEntryToOuterTypeEntry.get(entry);\n\n                    if (outerTypeEntry == null) {\n                        HashMap<String, Container.Entry> typeNameToEntry = new HashMap<>();\n                        HashMap<String, String> innerTypeNameToOuterTypeName = new HashMap<>();\n\n                        // Populate \"typeNameToEntry\" and \"innerTypeNameToOuterTypeName\"\n                        for (Container.Entry e : entry.getParent().getChildren()) {\n                            typeFactory = TypeFactoryService.getInstance().get(e);\n\n                            if (typeFactory != null) {\n                                type = typeFactory.make(api, e, null);\n\n                                if (type != null) {\n                                    typeNameToEntry.put(type.getName(), e);\n                                    if (type.getOuterName() != null) {\n                                        innerTypeNameToOuterTypeName.put(type.getName(), type.getOuterName());\n                                    }\n                                }\n                            }\n                        }\n\n                        // Search outer type entries and populate \"innerTypeEntryToOuterTypeEntry\"\n                        for (Map.Entry<String, String> e : innerTypeNameToOuterTypeName.entrySet()) {\n                            Container.Entry innerTypeEntry = typeNameToEntry.get(e.getKey());\n\n                            if (innerTypeEntry != null) {\n                                String outerTypeName = e.getValue();\n\n                                for (;;) {\n                                    String typeName = innerTypeNameToOuterTypeName.get(outerTypeName);\n                                    if (typeName != null) {\n                                        outerTypeName = typeName;\n                                    } else {\n                                        break;\n                                    }\n                                }\n\n                                outerTypeEntry = typeNameToEntry.get(outerTypeName);\n\n                                if (outerTypeEntry != null) {\n                                    innerTypeEntryToOuterTypeEntry.put(innerTypeEntry, outerTypeEntry);\n                                }\n                            }\n                        }\n\n                        // Get outer type entry\n                        outerTypeEntry = innerTypeEntryToOuterTypeEntry.get(entry);\n\n                        if (outerTypeEntry == null) {\n                            outerTypeEntry = entry;\n                        }\n                    }\n\n                    matchingOuterEntriesSet.add(outerTypeEntry);\n                } else{\n                    matchingOuterEntriesSet.add(entry);\n                }\n            }\n        }\n\n        return matchingOuterEntriesSet;\n    }\n\n    protected void filter(Indexes indexes, String pattern, int flags, Set<Container.Entry> matchingEntries) {\n        boolean declarations = ((flags & SearchInConstantPoolsView.SEARCH_DECLARATION) != 0);\n        boolean references = ((flags & SearchInConstantPoolsView.SEARCH_REFERENCE) != 0);\n\n        if ((flags & SearchInConstantPoolsView.SEARCH_TYPE) != 0) {\n            if (declarations)\n                match(indexes, \"typeDeclarations\", pattern,\n                      SearchInConstantPoolsController::matchTypeEntriesWithChar,\n                      SearchInConstantPoolsController::matchTypeEntriesWithString, matchingEntries);\n            if (references)\n                match(indexes, \"typeReferences\", pattern,\n                      SearchInConstantPoolsController::matchTypeEntriesWithChar,\n                      SearchInConstantPoolsController::matchTypeEntriesWithString, matchingEntries);\n        }\n\n        if ((flags & SearchInConstantPoolsView.SEARCH_CONSTRUCTOR) != 0) {\n            if (declarations)\n                match(indexes, \"constructorDeclarations\", pattern,\n                      SearchInConstantPoolsController::matchTypeEntriesWithChar,\n                      SearchInConstantPoolsController::matchTypeEntriesWithString, matchingEntries);\n            if (references)\n                match(indexes, \"constructorReferences\", pattern,\n                      SearchInConstantPoolsController::matchTypeEntriesWithChar,\n                      SearchInConstantPoolsController::matchTypeEntriesWithString, matchingEntries);\n        }\n\n        if ((flags & SearchInConstantPoolsView.SEARCH_METHOD) != 0) {\n            if (declarations)\n                match(indexes, \"methodDeclarations\", pattern,\n                      SearchInConstantPoolsController::matchWithChar,\n                      SearchInConstantPoolsController::matchWithString, matchingEntries);\n            if (references)\n                match(indexes, \"methodReferences\", pattern,\n                      SearchInConstantPoolsController::matchWithChar,\n                      SearchInConstantPoolsController::matchWithString, matchingEntries);\n        }\n\n        if ((flags & SearchInConstantPoolsView.SEARCH_FIELD) != 0) {\n            if (declarations)\n                match(indexes, \"fieldDeclarations\", pattern,\n                      SearchInConstantPoolsController::matchWithChar,\n                      SearchInConstantPoolsController::matchWithString, matchingEntries);\n            if (references)\n                match(indexes, \"fieldReferences\", pattern,\n                      SearchInConstantPoolsController::matchWithChar,\n                      SearchInConstantPoolsController::matchWithString, matchingEntries);\n        }\n\n        if ((flags & SearchInConstantPoolsView.SEARCH_STRING) != 0) {\n            if (declarations || references)\n                match(indexes, \"strings\", pattern,\n                      SearchInConstantPoolsController::matchWithChar,\n                      SearchInConstantPoolsController::matchWithString, matchingEntries);\n        }\n\n        if ((flags & SearchInConstantPoolsView.SEARCH_MODULE) != 0) {\n            if (declarations)\n                match(indexes, \"javaModuleDeclarations\", pattern,\n                        SearchInConstantPoolsController::matchWithChar,\n                        SearchInConstantPoolsController::matchWithString, matchingEntries);\n            if (references)\n                match(indexes, \"javaModuleReferences\", pattern,\n                        SearchInConstantPoolsController::matchWithChar,\n                        SearchInConstantPoolsController::matchWithString, matchingEntries);\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected void match(Indexes indexes, String indexName, String pattern,\n                         BiFunction<Character, Map<String, Collection>, Map<String, Collection>> matchWithCharFunction,\n                         BiFunction<String, Map<String, Collection>, Map<String, Collection>> matchWithStringFunction,\n                         Set<Container.Entry> matchingEntries) {\n        int patternLength = pattern.length();\n\n        if (patternLength > 0) {\n            String key = String.valueOf(indexes.hashCode()) + \"***\" + indexName + \"***\" + pattern;\n            Map<String, Collection> matchedEntries = cache.get(key);\n\n            if (matchedEntries == null) {\n                Map<String, Collection> index = indexes.getIndex(indexName);\n\n                if (index != null) {\n                    if (patternLength == 1) {\n                        matchedEntries = matchWithCharFunction.apply(pattern.charAt(0), index);\n                    } else {\n                        String lastKey = key.substring(0, key.length() - 1);\n                        Map<String, Collection> lastMatchedTypes = cache.get(lastKey);\n                        if (lastMatchedTypes != null) {\n                            matchedEntries = matchWithStringFunction.apply(pattern, lastMatchedTypes);\n                        } else {\n                            matchedEntries = matchWithStringFunction.apply(pattern, index);\n                        }\n                    }\n                }\n\n                // Cache matchingEntries\n                cache.put(key, matchedEntries);\n            }\n\n            if (matchedEntries != null) {\n                for (Collection<Container.Entry> entries : matchedEntries.values()) {\n                    matchingEntries.addAll(entries);\n                }\n            }\n        }\n    }\n\n    protected static Map<String, Collection> matchTypeEntriesWithChar(char c, Map<String, Collection> index) {\n        if ((c == '*') || (c == '?')) {\n            return index;\n        } else {\n            Map<String, Collection> map = new HashMap<>();\n\n            for (String typeName : index.keySet()) {\n                // Search last package separator\n                int lastPackageSeparatorIndex = typeName.lastIndexOf('/') + 1;\n                int lastTypeNameSeparatorIndex = typeName.lastIndexOf('$') + 1;\n                int lastIndex = Math.max(lastPackageSeparatorIndex, lastTypeNameSeparatorIndex);\n\n                if ((lastIndex < typeName.length()) && (typeName.charAt(lastIndex) == c)) {\n                    map.put(typeName, index.get(typeName));\n                }\n            }\n\n            return map;\n        }\n    }\n\n    protected static Map<String, Collection> matchTypeEntriesWithString(String pattern, Map<String, Collection> index) {\n        Pattern p = createPattern(pattern);\n        Map<String, Collection> map = new HashMap<>();\n\n        for (String typeName : index.keySet()) {\n            // Search last package separator\n            int lastPackageSeparatorIndex = typeName.lastIndexOf('/') + 1;\n            int lastTypeNameSeparatorIndex = typeName.lastIndexOf('$') + 1;\n            int lastIndex = Math.max(lastPackageSeparatorIndex, lastTypeNameSeparatorIndex);\n\n            if (p.matcher(typeName.substring(lastIndex)).matches()) {\n                map.put(typeName, index.get(typeName));\n            }\n        }\n\n        return map;\n    }\n\n    protected static Map<String, Collection> matchWithChar(char c, Map<String, Collection> index) {\n        if ((c == '*') || (c == '?')) {\n            return index;\n        } else {\n            Map<String, Collection> map = new HashMap<>();\n\n            for (String key : index.keySet()) {\n                if (!key.isEmpty() && (key.charAt(0) == c)) {\n                    map.put(key, index.get(key));\n                }\n            }\n\n            return map;\n        }\n    }\n\n    protected static Map<String, Collection> matchWithString(String pattern, Map<String, Collection> index) {\n        Pattern p = createPattern(pattern);\n        Map<String, Collection> map = new HashMap<>();\n\n        for (String key : index.keySet()) {\n            if (p.matcher(key).matches()) {\n                map.put(key, index.get(key));\n            }\n        }\n\n        return map;\n    }\n\n    /**\n     * Create a simple regular expression\n     *\n     * Rules:\n     *  '*'        matchTypeEntries 0 ou N characters\n     *  '?'        matchTypeEntries 1 character\n     */\n    protected static Pattern createPattern(String pattern) {\n        int patternLength = pattern.length();\n        StringBuilder sbPattern = new StringBuilder(patternLength * 2);\n\n        for (int i = 0; i < patternLength; i++) {\n            char c = pattern.charAt(i);\n\n            if (c == '*') {\n                sbPattern.append(\".*\");\n            } else if (c == '?') {\n                sbPattern.append('.');\n            } else if (c == '.') {\n                sbPattern.append(\"\\\\.\");\n            } else {\n                sbPattern.append(c);\n            }\n        }\n\n        sbPattern.append(\".*\");\n\n        return Pattern.compile(sbPattern.toString());\n    }\n\n    protected void onTypeSelected(URI uri, String pattern, int flags) {\n        // Open the single entry uri\n        Container.Entry entry = null;\n\n        for (DelegatingFilterContainer container : delegatingFilterContainers) {\n            entry = container.getEntry(uri);\n            if (entry != null)\n                break;\n        }\n\n        if (entry != null) {\n            StringBuilder sbPattern = new StringBuilder(200 + pattern.length());\n\n            sbPattern.append(\"highlightPattern=\");\n            sbPattern.append(pattern);\n            sbPattern.append(\"&highlightFlags=\");\n\n            if ((flags & SearchInConstantPoolsView.SEARCH_DECLARATION) != 0)\n                sbPattern.append('d');\n            if ((flags & SearchInConstantPoolsView.SEARCH_REFERENCE) != 0)\n                sbPattern.append('r');\n            if ((flags & SearchInConstantPoolsView.SEARCH_TYPE) != 0)\n                sbPattern.append('t');\n            if ((flags & SearchInConstantPoolsView.SEARCH_CONSTRUCTOR) != 0)\n                sbPattern.append('c');\n            if ((flags & SearchInConstantPoolsView.SEARCH_METHOD) != 0)\n                sbPattern.append('m');\n            if ((flags & SearchInConstantPoolsView.SEARCH_FIELD) != 0)\n                sbPattern.append('f');\n            if ((flags & SearchInConstantPoolsView.SEARCH_STRING) != 0)\n                sbPattern.append('s');\n            if ((flags & SearchInConstantPoolsView.SEARCH_MODULE) != 0)\n                sbPattern.append('M');\n\n            // TODO In a future release, add 'highlightScope' to display search results in correct type and inner-type\n            // def type = TypeFactoryService.instance.get(entry)?.make(api, entry, null)\n            // if (type) {\n            //     sbPattern.append('&highlightScope=')\n            //     sbPattern.append(type.name)\n            //\n            //     def query = sbPattern.toString()\n            //     def outerPath = UriUtil.getOuterPath(collectionOfFutureIndexes, entry, type)\n            //\n            //     openClosure(new URI(entry.uri.scheme, entry.uri.host, outerPath, query, null))\n            // } else {\n                String query = sbPattern.toString();\n                URI u = entry.getUri();\n\n                try {\n                    openCallback.accept(new URI(u.getScheme(), u.getHost(), u.getPath(), query, null));\n                } catch (URISyntaxException e) {\n                    assert ExceptionUtil.printStackTrace(e);\n                }\n            // }\n        }\n    }\n\n    // --- IndexesChangeListener --- //\n    public void indexesChanged(Collection<Future<Indexes>> collectionOfFutureIndexes) {\n        if (searchInConstantPoolsView.isVisible()) {\n            // Update the list of containers\n            this.collectionOfFutureIndexes = collectionOfFutureIndexes;\n            // And refresh\n            updateTree(searchInConstantPoolsView.getPattern(), searchInConstantPoolsView.getFlags());\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/controller/SelectLocationController.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.controller;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Type;\nimport org.jd.gui.model.container.DelegatingFilterContainer;\nimport org.jd.gui.service.type.TypeFactoryService;\nimport org.jd.gui.spi.TypeFactory;\nimport org.jd.gui.view.SelectLocationView;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.net.URI;\nimport java.util.*;\nimport java.util.function.Consumer;\n\npublic class SelectLocationController {\n    protected static final ContainerEntryComparator CONTAINER_ENTRY_COMPARATOR = new ContainerEntryComparator();\n\n    protected API api;\n    protected SelectLocationView selectLocationView;\n\n    public SelectLocationController(API api, JFrame mainFrame) {\n        this.api = api;\n        // Create UI\n        selectLocationView = new SelectLocationView(api, mainFrame);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public void show(Point location, Collection<Container.Entry> entries, Consumer<Container.Entry> selectedLocationCallback, Runnable closeCallback) {\n        // Show UI\n        HashMap<Container, ArrayList<Container.Entry>> map = new HashMap<>();\n\n        for (Container.Entry entry : entries) {\n            Container container = entry.getContainer();\n\n            // Search root container\n            while (true) {\n                Container parentContainer = container.getRoot().getParent().getContainer();\n                if (parentContainer.getRoot() == null) {\n                    break;\n                } else {\n                    container = parentContainer;\n                }\n            }\n\n            ArrayList<Container.Entry> list = map.get(container);\n\n            if (list == null) {\n                map.put(container, list=new ArrayList<>());\n            }\n\n            list.add(entry);\n        }\n\n        HashSet<DelegatingFilterContainer> delegatingFilterContainers = new HashSet<>();\n\n        for (Map.Entry<Container, ArrayList<Container.Entry>> mapEntry : map.entrySet()) {\n            Container container = mapEntry.getKey();\n            // Create a filtered container\n            // TODO In a future release, display matching types and inner-types, not only matching files\n            delegatingFilterContainers.add(new DelegatingFilterContainer(container, getOuterEntries(mapEntry.getValue())));\n        }\n\n        Consumer<URI> selectedEntryCallback = uri -> onLocationSelected(delegatingFilterContainers, uri, selectedLocationCallback);\n\n        selectLocationView.show(location, delegatingFilterContainers, entries.size(), selectedEntryCallback, closeCallback);\n    }\n\n    protected Collection<Container.Entry> getOuterEntries(Collection<Container.Entry> entries) {\n        HashMap<Container.Entry, Container.Entry> innerTypeEntryToOuterTypeEntry = new HashMap<>();\n        HashSet<Container.Entry> outerEntriesSet = new HashSet<>();\n\n        for (Container.Entry entry : entries) {\n            Container.Entry outerTypeEntry = null;\n            TypeFactory factory = TypeFactoryService.getInstance().get(entry);\n\n            if (factory != null) {\n                Type type = factory.make(api, entry, null);\n\n                if ((type != null) && (type.getOuterName() != null)) {\n                    outerTypeEntry = innerTypeEntryToOuterTypeEntry.get(entry);\n\n                    if (outerTypeEntry == null) {\n                        HashMap<String, Container.Entry> typeNameToEntry = new HashMap<>();\n                        HashMap<String, String> innerTypeNameToOuterTypeName = new HashMap<>();\n\n                        // Populate \"typeNameToEntry\" and \"innerTypeNameToOuterTypeName\"\n                        for (Container.Entry e : entry.getParent().getChildren()) {\n                            factory = TypeFactoryService.getInstance().get(e);\n\n                            if (factory != null) {\n                                type = factory.make(api, e, null);\n\n                                if (type != null) {\n                                    typeNameToEntry.put(type.getName(), e);\n                                    if (type.getOuterName() != null) {\n                                        innerTypeNameToOuterTypeName.put(type.getName(), type.getOuterName());\n                                    }\n                                }\n                            }\n                        }\n\n                        // Search outer type entries and populate \"innerTypeEntryToOuterTypeEntry\"\n                        for (Map.Entry<String, String> e : innerTypeNameToOuterTypeName.entrySet()) {\n                            Container.Entry innerTypeEntry = typeNameToEntry.get(e.getKey());\n\n                            if (innerTypeEntry != null) {\n                                String outerTypeName = e.getValue();\n\n                                for (;;) {\n                                    String typeName = innerTypeNameToOuterTypeName.get(outerTypeName);\n                                    if (typeName != null) {\n                                        outerTypeName = typeName;\n                                    } else {\n                                        break;\n                                    }\n                                }\n\n                                outerTypeEntry = typeNameToEntry.get(outerTypeName);\n\n                                if (outerTypeEntry != null) {\n                                    innerTypeEntryToOuterTypeEntry.put(innerTypeEntry, outerTypeEntry);\n                                }\n                            }\n                        }\n\n                        // Get outer type entry\n                        outerTypeEntry = innerTypeEntryToOuterTypeEntry.get(entry);\n                    }\n                }\n            }\n\n            if (outerTypeEntry != null) {\n                outerEntriesSet.add(outerTypeEntry);\n            } else {\n                outerEntriesSet.add(entry);\n            }\n        }\n\n        // Return outer type entries sorted by path\n        ArrayList<Container.Entry> result = new ArrayList<>(outerEntriesSet);\n\n        result.sort(CONTAINER_ENTRY_COMPARATOR);\n\n        return result;\n    }\n\n    protected void onLocationSelected(Set<DelegatingFilterContainer> delegatingFilterContainers, URI uri, Consumer<Container.Entry> selectedLocationCallback) {\n        // Open the single entry uri\n        Container.Entry entry = null;\n\n        for (DelegatingFilterContainer container : delegatingFilterContainers) {\n            entry = container.getEntry(uri);\n            if (entry != null) {\n                break;\n            }\n        }\n\n        if (entry != null) {\n            selectedLocationCallback.accept(entry);\n        }\n    }\n\n    protected static class ContainerEntryComparator implements Comparator<Container.Entry> {\n        @Override\n        public int compare(Container.Entry e1, Container.Entry e2) {\n            return e1.getPath().compareTo(e2.getPath());\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/model/configuration/Configuration.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.model.configuration;\n\nimport org.jd.gui.Constants;\n\nimport java.awt.*;\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\npublic class Configuration {\n\tprotected Point mainWindowLocation;\n    protected Dimension mainWindowSize;\n    protected boolean mainWindowMaximize;\n    protected String lookAndFeel;\n\n    protected List<File> recentFiles = new ArrayList<>();\n\n    protected File recentLoadDirectory;\n    protected File recentSaveDirectory;\n\n    protected Map<String, String> preferences = new HashMap<>();\n\n    public Point getMainWindowLocation() {\n        return mainWindowLocation;\n    }\n\n    public Dimension getMainWindowSize() {\n        return mainWindowSize;\n    }\n\n    public boolean isMainWindowMaximize() {\n        return mainWindowMaximize;\n    }\n\n    public String getLookAndFeel() {\n        return lookAndFeel;\n    }\n\n    public List<File> getRecentFiles() {\n        return recentFiles;\n    }\n\n    public File getRecentLoadDirectory() {\n        return recentLoadDirectory;\n    }\n\n    public File getRecentSaveDirectory() {\n        return recentSaveDirectory;\n    }\n\n    public Map<String, String> getPreferences() {\n        return preferences;\n    }\n\n    public void setMainWindowLocation(Point mainWindowLocation) {\n        this.mainWindowLocation = mainWindowLocation;\n    }\n\n    public void setMainWindowSize(Dimension mainWindowSize) {\n        this.mainWindowSize = mainWindowSize;\n    }\n\n    public void setMainWindowMaximize(boolean mainWindowMaximize) {\n        this.mainWindowMaximize = mainWindowMaximize;\n    }\n\n    public void setLookAndFeel(String lookAndFeel) {\n        this.lookAndFeel = lookAndFeel;\n    }\n\n    public void setRecentFiles(List<File> recentFiles) {\n        this.recentFiles = recentFiles;\n    }\n\n    public void setRecentLoadDirectory(File recentLoadDirectory) {\n        this.recentLoadDirectory = recentLoadDirectory;\n    }\n\n    public void setRecentSaveDirectory(File recentSaveDirectory) {\n        this.recentSaveDirectory = recentSaveDirectory;\n    }\n\n    public void setPreferences(Map<String, String> preferences) {\n        this.preferences = preferences;\n    }\n\n    public void addRecentFile(File file) {\n        recentFiles.remove(file);\n        recentFiles.add(0, file);\n        if (recentFiles.size() > Constants.MAX_RECENT_FILES) {\n            recentFiles.remove(Constants.MAX_RECENT_FILES);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/model/container/DelegatingFilterContainer.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.model.container;\n\nimport org.jd.gui.api.model.Container;\n\nimport java.io.InputStream;\nimport java.net.URI;\nimport java.util.*;\n\npublic class DelegatingFilterContainer implements Container {\n    protected static final URI DEFAULT_ROOT_URI = URI.create(\"file:.\");\n\n    protected Container container;\n    protected DelegatedEntry root;\n\n    protected Set<URI> validEntries = new HashSet<>();\n    protected Map<URI, DelegatedEntry> uriToDelegatedEntry = new HashMap<>();\n    protected Map<URI, DelegatedContainer> uriToDelegatedContainer = new HashMap<>();\n\n    public DelegatingFilterContainer(Container container, Collection<Entry> entries) {\n        this.container = container;\n        this.root = getDelegatedEntry(container.getRoot());\n\n        for (Entry entry : entries) {\n            while ((entry != null) && !validEntries.contains(entry.getUri())) {\n                validEntries.add(entry.getUri());\n                entry = entry.getParent();\n            }\n        }\n    }\n\n    @Override public String getType() { return container.getType(); }\n    @Override public Container.Entry getRoot() { return root; }\n\n    public Container.Entry getEntry(URI uri) { return uriToDelegatedEntry.get(uri); }\n    public Set<URI> getUris() { return validEntries; }\n\n    protected DelegatedEntry getDelegatedEntry(Container.Entry entry) {\n        URI uri = entry.getUri();\n        DelegatedEntry delegatedEntry = uriToDelegatedEntry.get(uri);\n        if (delegatedEntry == null) {\n            uriToDelegatedEntry.put(uri, delegatedEntry =new DelegatedEntry(entry));\n        }\n        return delegatedEntry;\n    }\n\n    protected DelegatedContainer getDelegatedContainer(Container container) {\n        Entry root = container.getRoot();\n        URI uri = (root == null) ? DEFAULT_ROOT_URI : root.getUri();\n        DelegatedContainer delegatedContainer = uriToDelegatedContainer.get(uri);\n        if (delegatedContainer == null) {\n            uriToDelegatedContainer.put(uri, delegatedContainer =new DelegatedContainer(container));\n        }\n        return delegatedContainer;\n    }\n\n    protected class DelegatedEntry implements Entry, Comparable<DelegatedEntry> {\n        protected Entry entry;\n        protected Collection<Entry> children;\n\n        public DelegatedEntry(Entry entry) {\n            this.entry = entry;\n        }\n\n        @Override public Container getContainer() { return getDelegatedContainer(entry.getContainer()); }\n        @Override public Entry getParent() { return getDelegatedEntry(entry.getParent()); }\n        @Override public URI getUri() { return entry.getUri(); }\n        @Override public String getPath() { return entry.getPath(); }\n        @Override public boolean isDirectory() { return entry.isDirectory(); }\n        @Override public long length() { return entry.length(); }\n        @Override public InputStream getInputStream() { return entry.getInputStream(); }\n\n        @Override\n        public Collection<Entry> getChildren() {\n            if (children == null) {\n                children = new ArrayList<>();\n                for (Entry child : entry.getChildren()) {\n                    if (validEntries.contains(child.getUri())) {\n                        children.add(getDelegatedEntry(child));\n                    }\n                }\n            }\n            return children;\n        }\n\n        @Override\n        public int compareTo(DelegatedEntry other) {\n            if (entry.isDirectory()) {\n                if (!other.isDirectory()) {\n                    return -1;\n                }\n            } else {\n                if (other.isDirectory()) {\n                    return 1;\n                }\n            }\n            return entry.getPath().compareTo(other.getPath());\n        }\n    }\n\n    protected class DelegatedContainer implements Container {\n        protected Container container;\n\n        public DelegatedContainer(Container container) {\n            this.container = container;\n        }\n\n        @Override public String getType() { return container.getType(); }\n        @Override public Entry getRoot() { return getDelegatedEntry(container.getRoot()); }\n    }\n}"
  },
  {
    "path": "app/src/main/java/org/jd/gui/model/history/History.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.model.history;\n\nimport java.net.URI;\nimport java.util.ArrayList;\n\npublic class History {\n    protected URI            current = null;\n    protected ArrayList<URI> backward = new ArrayList<>();\n    protected ArrayList<URI> forward = new ArrayList<>();\n\n    public void add(URI uri) {\n        if (current == null) {\n            // Init history\n            forward.clear();\n            current = uri;\n            return;\n        }\n\n        if (current.equals(uri)) {\n            // Already stored -> Nothing to do\n            return;\n        }\n\n        if (uri.getPath().toString().equals(current.getPath().toString())) {\n            if ((uri.getFragment() == null) && (uri.getQuery() == null)) {\n                // Ignore\n            } else if ((current.getFragment() == null) && (current.getQuery() == null)) {\n                // Replace current URI\n                current = uri;\n            } else {\n                // Store URI\n                forward.clear();\n                backward.add(current);\n                current = uri;\n            }\n            return;\n        }\n\n        if (uri.toString().startsWith(current.toString())) {\n            // Replace current URI\n            current = uri;\n            return;\n        }\n\n        if (current.toString().startsWith(uri.toString())) {\n            // Parent URI -> Nothing to do\n            return;\n        }\n\n        // Store URI\n        forward.clear();\n        backward.add(current);\n        current = uri;\n    }\n\n    public URI backward() {\n        if (! backward.isEmpty()) {\n            forward.add(current);\n            int size = backward.size();\n            current = backward.remove(size-1);\n        }\n        return current;\n    }\n\n    public URI forward() {\n        if (! forward.isEmpty()) {\n            backward.add(current);\n            int size = forward.size();\n            current = forward.remove(size-1);\n        }\n        return current;\n    }\n\n    public boolean canBackward() { return !backward.isEmpty(); }\n    public boolean canForward() { return !forward.isEmpty(); }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/service/actions/ContextualActionsFactoryService.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.actions;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.service.extension.ExtensionService;\nimport org.jd.gui.spi.ContextualActionsFactory;\n\nimport javax.swing.*;\nimport java.util.*;\n\npublic class ContextualActionsFactoryService {\n    protected static final ContextualActionsFactoryService CONTEXTUAL_ACTIONS_FACTORY_SERVICE = new ContextualActionsFactoryService();\n\n    public static ContextualActionsFactoryService getInstance() { return CONTEXTUAL_ACTIONS_FACTORY_SERVICE; }\n\n    protected static final ActionNameComparator COMPARATOR = new ActionNameComparator();\n\n    protected final Collection<ContextualActionsFactory> providers = ExtensionService.getInstance().load(ContextualActionsFactory.class);\n\n    public Collection<Action> get(API api, Container.Entry entry, String fragment) {\n        HashMap<String, ArrayList<Action>> mapActions = new HashMap<>();\n\n        for (ContextualActionsFactory provider : providers) {\n            Collection<Action> actions = provider.make(api, entry, fragment);\n\n            for (Action action : actions) {\n                String groupName = (String)action.getValue(ContextualActionsFactory.GROUP_NAME);\n                ArrayList<Action> list = mapActions.get(groupName);\n\n                if (list == null) {\n                    mapActions.put(groupName, list=new ArrayList<>());\n                }\n\n                list.add(action);\n            }\n        }\n\n        if (!mapActions.isEmpty()) {\n            ArrayList<Action> result = new ArrayList<>();\n\n            // Sort by group names\n            ArrayList<String> groupNames = new ArrayList<>(mapActions.keySet());\n            Collections.sort(groupNames);\n\n            for (String groupName : groupNames) {\n                if (! result.isEmpty()) {\n                    // Add 'null' to mark a separator\n                    result.add(null);\n                }\n                // Sort by names\n                ArrayList<Action> actions = mapActions.get(groupName);\n                Collections.sort(actions, COMPARATOR);\n                result.addAll(actions);\n            }\n\n            return result;\n        } else {\n            return Collections.emptyList();\n        }\n    }\n\n    protected static class ActionNameComparator implements Comparator<Action> {\n        @Override\n        public int compare(Action a1, Action a2) {\n            String n1 = (String)a1.getValue(Action.NAME);\n            if (n1 == null) {\n                n1 = \"\";\n            }\n\n            String n2 = (String)a2.getValue(Action.NAME);\n            if (n2 == null) {\n                n2 = \"\";\n            }\n\n            return n1.compareTo(n2);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/service/configuration/ConfigurationPersister.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.configuration;\n\nimport org.jd.gui.model.configuration.Configuration;\n\npublic interface ConfigurationPersister {\n    Configuration load();\n\n    void save(Configuration configuration);\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/service/configuration/ConfigurationPersisterService.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.configuration;\n\npublic class ConfigurationPersisterService {\n    protected static final ConfigurationPersisterService CONFIGURATION_PERSISTER_SERVICE = new ConfigurationPersisterService();\n\n    protected ConfigurationPersister provider = new ConfigurationXmlPersisterProvider();\n\n    public static ConfigurationPersisterService getInstance() { return CONFIGURATION_PERSISTER_SERVICE; }\n\n    protected ConfigurationPersisterService() {}\n\n    public ConfigurationPersister get() {\n        return provider;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/service/configuration/ConfigurationXmlPersisterProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.configuration;\n\nimport org.jd.gui.Constants;\nimport org.jd.gui.model.configuration.Configuration;\nimport org.jd.gui.service.platform.PlatformService;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport javax.swing.*;\nimport javax.xml.stream.*;\nimport java.awt.*;\nimport java.io.*;\nimport java.net.URL;\nimport java.util.*;\nimport java.util.List;\nimport java.util.jar.Manifest;\n\npublic class ConfigurationXmlPersisterProvider implements ConfigurationPersister {\n    protected static final String ERROR_BACKGROUND_COLOR = \"JdGuiPreferences.errorBackgroundColor\";\n    protected static final String JD_CORE_VERSION = \"JdGuiPreferences.jdCoreVersion\";\n\n    protected static final File FILE = getConfigFile();\n\n    protected static File getConfigFile() {\n        String configFilePath = System.getProperty(Constants.CONFIG_FILENAME);\n\n        if (configFilePath != null) {\n            File configFile = new File(configFilePath);\n            if (configFile.exists()) {\n                return configFile;\n            }\n        }\n\n        if (PlatformService.getInstance().isLinux()) {\n            // See: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html\n            String xdgConfigHome = System.getenv(\"XDG_CONFIG_HOME\");\n            if (xdgConfigHome != null) {\n                File xdgConfigHomeFile = new File(xdgConfigHome);\n                if (xdgConfigHomeFile.exists()) {\n                    return new File(xdgConfigHomeFile, Constants.CONFIG_FILENAME);\n                }\n            }\n\n            File userConfigFile = new File(System.getProperty(\"user.home\"), \".config\");\n            if (userConfigFile.exists()) {\n                return new File(userConfigFile, Constants.CONFIG_FILENAME);\n            }\n        } else if (PlatformService.getInstance().isWindows()) {\n            // See: http://blogs.msdn.com/b/patricka/archive/2010/03/18/where-should-i-store-my-data-and-configuration-files-if-i-target-multiple-os-versions.aspx\n            String roamingConfigHome = System.getenv(\"APPDATA\");\n            if (roamingConfigHome != null) {\n                File roamingConfigHomeFile = new File(roamingConfigHome);\n                if (roamingConfigHomeFile.exists()) {\n                    return new File(roamingConfigHomeFile, Constants.CONFIG_FILENAME);\n                }\n            }\n        }\n\n        return new File(Constants.CONFIG_FILENAME);\n    }\n\n    @Override\n    public Configuration load() {\n        // Default values\n        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();\n\n        int w = (screenSize.width>Constants.DEFAULT_WIDTH) ? Constants.DEFAULT_WIDTH : screenSize.width;\n        int h = (screenSize.height>Constants.DEFAULT_HEIGHT) ? Constants.DEFAULT_HEIGHT : screenSize.height;\n        int x = (screenSize.width-w)/2;\n        int y = (screenSize.height-h)/2;\n\n        Configuration config = new Configuration();\n        config.setMainWindowLocation(new Point(x, y));\n        config.setMainWindowSize(new Dimension(w, h));\n        config.setMainWindowMaximize(false);\n\n        String defaultLaf = System.getProperty(\"swing.defaultlaf\");\n\n        config.setLookAndFeel((defaultLaf != null) ? defaultLaf : UIManager.getSystemLookAndFeelClassName());\n\n        File recentSaveDirectory = new File(System.getProperty(\"user.dir\"));\n\n        config.setRecentLoadDirectory(recentSaveDirectory);\n        config.setRecentSaveDirectory(recentSaveDirectory);\n\n        if (FILE.exists()) {\n            try (FileInputStream fis = new FileInputStream(FILE)) {\n                XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(fis);\n\n                // Load values\n                String name = \"\";\n                Stack<String> names = new Stack<>();\n                List<File> recentFiles = new ArrayList<>();\n                boolean maximize = false;\n                Map<String, String> preferences = config.getPreferences();\n\n                while (reader.hasNext()) {\n                    switch (reader.next()) {\n                        case XMLStreamConstants.START_ELEMENT:\n                            names.push(name);\n                            name += '/' + reader.getLocalName();\n                            switch (name) {\n                                case \"/configuration/gui/mainWindow/location\":\n                                    x = Integer.parseInt(reader.getAttributeValue(null, \"x\"));\n                                    y = Integer.parseInt(reader.getAttributeValue(null, \"y\"));\n                                    break;\n                                case \"/configuration/gui/mainWindow/size\":\n                                    w = Integer.parseInt(reader.getAttributeValue(null, \"w\"));\n                                    h = Integer.parseInt(reader.getAttributeValue(null, \"h\"));\n                                    break;\n                            }\n                            break;\n                        case XMLStreamConstants.END_ELEMENT:\n                            name = names.pop();\n                            break;\n                        case XMLStreamConstants.CHARACTERS:\n                            switch (name) {\n                                case \"/configuration/recentFilePaths/filePath\":\n                                    File file = new File(reader.getText().trim());\n                                    if (file.exists()) {\n                                        recentFiles.add(file);\n                                    }\n                                    break;\n                                case \"/configuration/recentDirectories/loadPath\":\n                                    file = new File(reader.getText().trim());\n                                    if (file.exists()) {\n                                        config.setRecentLoadDirectory(file);\n                                    }\n                                    break;\n                                case \"/configuration/recentDirectories/savePath\":\n                                    file = new File(reader.getText().trim());\n                                    if (file.exists()) {\n                                        config.setRecentSaveDirectory(file);\n                                    }\n                                    break;\n                                case \"/configuration/gui/lookAndFeel\":\n                                    config.setLookAndFeel(reader.getText().trim());\n                                    break;\n                                case \"/configuration/gui/mainWindow/maximize\":\n                                    maximize = Boolean.parseBoolean(reader.getText().trim());\n                                    break;\n                                default:\n                                    if (name.startsWith(\"/configuration/preferences/\")) {\n                                        String key = name.substring(\"/configuration/preferences/\".length());\n                                        preferences.put(key, reader.getText().trim());\n                                    }\n                                    break;\n                            }\n                            break;\n                    }\n                }\n\n                if (recentFiles.size() > Constants.MAX_RECENT_FILES) {\n                    // Truncate\n                    recentFiles = recentFiles.subList(0, Constants.MAX_RECENT_FILES);\n                }\n                config.setRecentFiles(recentFiles);\n\n                if ((x >= 0) && (y >= 0) && (x + w < screenSize.width) && (y + h < screenSize.height)) {\n                    // Update preferences\n                    config.setMainWindowLocation(new Point(x, y));\n                    config.setMainWindowSize(new Dimension(w, h));\n                    config.setMainWindowMaximize(maximize);\n                }\n\n                reader.close();\n            } catch (Exception e) {\n                assert ExceptionUtil.printStackTrace(e);\n            }\n        }\n\n        if (! config.getPreferences().containsKey(ERROR_BACKGROUND_COLOR)) {\n            config.getPreferences().put(ERROR_BACKGROUND_COLOR, \"0xFF6666\");\n        }\n\n        config.getPreferences().put(JD_CORE_VERSION, getJdCoreVersion());\n\n        return config;\n    }\n\n    protected String getJdCoreVersion() {\n        try {\n            Enumeration<URL> enumeration = ConfigurationXmlPersisterProvider.class.getClassLoader().getResources(\"META-INF/MANIFEST.MF\");\n\n            while (enumeration.hasMoreElements()) {\n                try (InputStream is = enumeration.nextElement().openStream()) {\n                    String attribute = new Manifest(is).getMainAttributes().getValue(\"JD-Core-Version\");\n                    if (attribute != null) {\n                        return attribute;\n                    }\n                }\n            }\n        } catch (IOException e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n\n        return \"SNAPSHOT\";\n    }\n\n    @Override\n    public void save(Configuration configuration) {\n        Point l = configuration.getMainWindowLocation();\n        Dimension s = configuration.getMainWindowSize();\n\n        try (FileOutputStream fos = new FileOutputStream(FILE)) {\n            XMLStreamWriter writer = XMLOutputFactory.newInstance().createXMLStreamWriter(fos);\n\n            // Save values\n            writer.writeStartDocument();\n            writer.writeCharacters(\"\\n\");\n            writer.writeStartElement(\"configuration\");\n            writer.writeCharacters(\"\\n\\t\");\n\n            writer.writeStartElement(\"gui\");\n            writer.writeCharacters(\"\\n\\t\\t\");\n                writer.writeStartElement(\"mainWindow\");\n                writer.writeCharacters(\"\\n\\t\\t\\t\");\n                    writer.writeStartElement(\"location\");\n                        writer.writeAttribute(\"x\", String.valueOf(l.x));\n                        writer.writeAttribute(\"y\", String.valueOf(l.y));\n                    writer.writeEndElement();\n                    writer.writeCharacters(\"\\n\\t\\t\\t\");\n                    writer.writeStartElement(\"size\");\n                        writer.writeAttribute(\"w\", String.valueOf(s.width));\n                        writer.writeAttribute(\"h\", String.valueOf(s.height));\n                    writer.writeEndElement();\n                    writer.writeCharacters(\"\\n\\t\\t\\t\");\n                    writer.writeStartElement(\"maximize\");\n                        writer.writeCharacters(String.valueOf(configuration.isMainWindowMaximize()));\n                    writer.writeEndElement();\n                    writer.writeCharacters(\"\\n\\t\\t\");\n                writer.writeEndElement();\n                writer.writeCharacters(\"\\n\\t\\t\");\n                writer.writeStartElement(\"lookAndFeel\");\n                    writer.writeCharacters(configuration.getLookAndFeel());\n                writer.writeEndElement();\n                writer.writeCharacters(\"\\n\\t\");\n            writer.writeEndElement();\n            writer.writeCharacters(\"\\n\\t\");\n\n            writer.writeStartElement(\"recentFilePaths\");\n\n            for (File recentFile : configuration.getRecentFiles()) {\n                writer.writeCharacters(\"\\n\\t\\t\");\n                writer.writeStartElement(\"filePath\");\n                    writer.writeCharacters(recentFile.getAbsolutePath());\n                writer.writeEndElement();\n            }\n\n            writer.writeCharacters(\"\\n\\t\");\n            writer.writeEndElement();\n            writer.writeCharacters(\"\\n\\t\");\n\n            writer.writeStartElement(\"recentDirectories\");\n            writer.writeCharacters(\"\\n\\t\\t\");\n                writer.writeStartElement(\"loadPath\");\n                    writer.writeCharacters(configuration.getRecentLoadDirectory().getAbsolutePath());\n                writer.writeEndElement();\n                writer.writeCharacters(\"\\n\\t\\t\");\n                writer.writeStartElement(\"savePath\");\n                    writer.writeCharacters(configuration.getRecentSaveDirectory().getAbsolutePath());\n                writer.writeEndElement();\n                writer.writeCharacters(\"\\n\\t\");\n            writer.writeEndElement();\n            writer.writeCharacters(\"\\n\\t\");\n\n            writer.writeStartElement(\"preferences\");\n\n            for (Map.Entry<String, String> preference : configuration.getPreferences().entrySet()) {\n                writer.writeCharacters(\"\\n\\t\\t\");\n                writer.writeStartElement(preference.getKey());\n                    writer.writeCharacters(preference.getValue());\n                writer.writeEndElement();\n            }\n\n            writer.writeCharacters(\"\\n\\t\");\n            writer.writeEndElement();\n            writer.writeCharacters(\"\\n\");\n\n            writer.writeEndElement();\n            writer.writeEndDocument();\n            writer.close();\n        } catch (Exception e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/service/container/ContainerFactoryService.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.container;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.service.extension.ExtensionService;\nimport org.jd.gui.spi.ContainerFactory;\n\nimport java.nio.file.Path;\nimport java.util.Collection;\n\npublic class ContainerFactoryService {\n    protected static final ContainerFactoryService CONTAINER_FACTORY_SERVICE = new ContainerFactoryService();\n\n    public static ContainerFactoryService getInstance() { return CONTAINER_FACTORY_SERVICE; }\n\n    protected final Collection<ContainerFactory> providers = ExtensionService.getInstance().load(ContainerFactory.class);\n\n    public ContainerFactory get(API api, Path rootPath) {\n        for (ContainerFactory containerFactory : providers) {\n            if (containerFactory.accept(api, rootPath)) {\n                return containerFactory;\n            }\n        }\n\n        return null;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/service/extension/ExtensionService.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.extension;\n\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport java.io.File;\nimport java.net.URI;\nimport java.net.URL;\nimport java.net.URLClassLoader;\nimport java.util.*;\n\npublic class ExtensionService {\n    protected static final ExtensionService EXTENSION_SERVICE = new ExtensionService();\n    protected static final UrlComparator URL_COMPARATOR = new UrlComparator();\n\n    protected ClassLoader extensionClassLoader;\n\n    public static ExtensionService getInstance() {\n        return EXTENSION_SERVICE;\n    }\n\n    protected ExtensionService() {\n        try {\n            URI jarUri = ExtensionService.class.getProtectionDomain().getCodeSource().getLocation().toURI();\n            File baseDirectory = new File(jarUri).getParentFile();\n            File extDirectory = new File(baseDirectory, \"ext\");\n\n            if (extDirectory.exists() && extDirectory.isDirectory()) {\n                ArrayList<URL> urls = new ArrayList<>();\n\n                searchJarAndMetaInf(urls, extDirectory);\n\n                if (!urls.isEmpty()) {\n                    URL[] array = urls.toArray(new URL[urls.size()]);\n                    Arrays.sort(array, URL_COMPARATOR);\n                    extensionClassLoader = new URLClassLoader(array, ExtensionService.class.getClassLoader());\n                }\n            }\n        } catch (Exception e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n\n        extensionClassLoader = ExtensionService.class.getClassLoader();\n    }\n\n    protected void searchJarAndMetaInf(List<URL> urls, File directory) throws Exception {\n        File metaInf = new File(directory, \"META-INF\");\n\n        if (metaInf.exists() && metaInf.isDirectory()) {\n            urls.add(directory.toURI().toURL());\n        } else {\n            for (File child : directory.listFiles()) {\n                if (child.isDirectory()) {\n                    searchJarAndMetaInf(urls, child);\n                } else if (child.getName().toLowerCase().endsWith(\".jar\")) {\n                    urls.add(new URL(\"jar\", \"\", child.toURI().toURL().toString() + \"!/\"));\n                }\n            }\n        }\n    }\n\n    public <T> Collection<T> load(Class<T> service) {\n        ArrayList<T> list = new ArrayList<>();\n        Iterator<T> iterator = ServiceLoader.load(service, extensionClassLoader).iterator();\n\n        while (iterator.hasNext()) {\n            list.add(iterator.next());\n        }\n\n        return list;\n    }\n\n    protected static class UrlComparator implements Comparator<URL> {\n        @Override\n        public int compare(URL url1, URL url2) {\n            return url1.getPath().compareTo(url2.getPath());\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/service/fileloader/FileLoaderService.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.fileloader;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.service.extension.ExtensionService;\nimport org.jd.gui.spi.FileLoader;\n\nimport java.io.File;\nimport java.util.Collection;\nimport java.util.HashMap;\n\npublic class FileLoaderService {\n    protected static final FileLoaderService FILE_LOADER_SERVICE = new FileLoaderService();\n\n    public static FileLoaderService getInstance() { return FILE_LOADER_SERVICE; }\n\n    protected final Collection<FileLoader> providers = ExtensionService.getInstance().load(FileLoader.class);\n\n    protected HashMap<String, FileLoader> mapProviders = new HashMap<>();\n\n    protected FileLoaderService() {\n        for (FileLoader provider : providers) {\n            for (String extension : provider.getExtensions()) {\n                mapProviders.put(extension, provider);\n            }\n        }\n    }\n\n    public FileLoader get(API api, File file) {\n        String name = file.getName();\n        int lastDot = name.lastIndexOf('.');\n        String extension = name.substring(lastDot+1);\n        FileLoader provider = mapProviders.get(extension);\n        return provider;\n    }\n\n    public HashMap<String, FileLoader> getMapProviders() {\n        return mapProviders;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/service/indexer/IndexerService.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.indexer;\n\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.service.extension.ExtensionService;\nimport org.jd.gui.spi.Indexer;\n\nimport java.util.Collection;\nimport java.util.HashMap;\n\npublic class IndexerService {\n    protected static final IndexerService INDEXER_SERVICE = new IndexerService();\n\n    public static IndexerService getInstance() { return INDEXER_SERVICE; }\n\n    protected HashMap<String, Indexers> mapProviders = new HashMap<>();\n\n    protected IndexerService() {\n        Collection<Indexer> providers = ExtensionService.getInstance().load(Indexer.class);\n\n        for (Indexer provider : providers) {\n            for (String selector : provider.getSelectors()) {\n                Indexers indexers = mapProviders.get(selector);\n\n                if (indexers == null) {\n                    mapProviders.put(selector, indexers=new Indexers());\n                }\n\n                indexers.add(provider);\n            }\n        }\n    }\n\n    public Indexer get(Container.Entry entry) {\n        Indexer indexer = get(entry.getContainer().getType(), entry);\n        return (indexer != null) ? indexer : get(\"*\", entry);\n    }\n\n    protected Indexer get(String containerType, Container.Entry entry) {\n        String path = entry.getPath();\n        String type = entry.isDirectory() ? \"dir\" : \"file\";\n        String prefix = containerType + ':' + type;\n        Indexer indexer = null;\n        Indexers indexers = mapProviders.get(prefix + ':' + path);\n\n        if (indexers != null) {\n            indexer = indexers.match(path);\n        }\n\n        if (indexer == null) {\n            int lastSlashIndex = path.lastIndexOf('/');\n            String name = path.substring(lastSlashIndex+1);\n\n            indexers = mapProviders.get(prefix + \":*/\" + name);\n            if (indexers != null) {\n                indexer = indexers.match(path);\n            }\n\n            if (indexer == null) {\n                int index = name.lastIndexOf('.');\n\n                if (index != -1) {\n                    String extension = name.substring(index + 1);\n\n                    indexers = mapProviders.get(prefix + \":*.\" + extension);\n                    if (indexers != null) {\n                        indexer = indexers.match(path);\n                    }\n                }\n\n                if (indexer == null) {\n                    indexers = mapProviders.get(prefix + \":*\");\n                    if (indexers != null) {\n                        indexer = indexers.match(path);\n                    }\n                }\n            }\n        }\n\n        return indexer;\n    }\n\n    protected static class Indexers {\n        protected HashMap<String, Indexer> indexers = new HashMap<>();\n        protected Indexer defaultIndexer;\n\n        public void add(Indexer indexer) {\n            if (indexer.getPathPattern() != null) {\n                indexers.put(indexer.getPathPattern().pattern(), indexer);\n            } else {\n                defaultIndexer = indexer;\n            }\n        }\n\n        public Indexer match(String path) {\n            for (Indexer indexer : indexers.values()) {\n                if (indexer.getPathPattern().matcher(path).matches()) {\n                    return indexer;\n                }\n            }\n            return defaultIndexer;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/service/mainpanel/ContainerPanelFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.mainpanel;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContentIndexable;\nimport org.jd.gui.api.feature.SourcesSavable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.spi.Indexer;\nimport org.jd.gui.spi.PanelFactory;\nimport org.jd.gui.spi.SourceSaver;\nimport org.jd.gui.spi.TreeNodeFactory;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.view.component.panel.TreeTabbedPanel;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport javax.swing.tree.DefaultTreeModel;\nimport java.io.IOException;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.nio.file.FileSystem;\nimport java.nio.file.FileSystems;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.*;\n\npublic class ContainerPanelFactoryProvider implements PanelFactory {\n    protected static final String[] TYPES = { \"default\" };\n\n\t@Override public String[] getTypes() { return TYPES; }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends JComponent & UriGettable> T make(API api, Container container) {\n        return (T)new ContainerPanel(api, container);\n\t}\n\n    protected class ContainerPanel extends TreeTabbedPanel implements ContentIndexable, SourcesSavable {\n        protected Container.Entry entry;\n\n        public ContainerPanel(API api, Container container) {\n            super(api, container.getRoot().getParent().getUri());\n\n            this.entry = container.getRoot().getParent();\n\n            DefaultMutableTreeNode root = new DefaultMutableTreeNode();\n\n            for (Container.Entry entry : container.getRoot().getChildren()) {\n                TreeNodeFactory factory = api.getTreeNodeFactory(entry);\n                if (factory != null) {\n                    root.add(factory.make(api, entry));\n                }\n            }\n\n            tree.setModel(new DefaultTreeModel(root));\n        }\n\n        // --- ContentIndexable --- //\n        @Override\n        public Indexes index(API api) {\n            HashMap<String, Map<String, Collection>> map = new HashMap<>();\n            DelegatedMapMapWithDefault mapWithDefault = new DelegatedMapMapWithDefault(map);\n\n            // Index populating value automatically\n            Indexes indexesWithDefault = name -> mapWithDefault.get(name);\n\n            // Index entry\n            Indexer indexer = api.getIndexer(entry);\n\n            if (indexer != null) {\n                indexer.index(api, entry, indexesWithDefault);\n            }\n\n            // To prevent memory leaks, return an index without the 'populate' behaviour\n            return name -> map.get(name);\n        }\n\n        // --- SourcesSavable --- //\n        @Override\n        public String getSourceFileName() {\n            SourceSaver saver = api.getSourceSaver(entry);\n\n            if (saver != null) {\n                String path = saver.getSourcePath(entry);\n                int index = path.lastIndexOf('/');\n                return path.substring(index+1);\n            } else {\n                return null;\n            }\n        }\n\n        @Override\n        public int getFileCount() {\n            SourceSaver saver = api.getSourceSaver(entry);\n            return (saver != null) ? saver.getFileCount(api, entry) : 0;\n        }\n\n        @Override\n        public void save(API api, Controller controller, Listener listener, Path path) {\n            try {\n                Path parentPath = path.getParent();\n\n                if ((parentPath != null) && !Files.exists(parentPath)) {\n                    Files.createDirectories(parentPath);\n                }\n\n                URI uri = path.toUri();\n                URI archiveUri = new URI(\"jar:\" + uri.getScheme(), uri.getHost(), uri.getPath() + \"!/\", null);\n\n                try (FileSystem archiveFs = FileSystems.newFileSystem(archiveUri, Collections.singletonMap(\"create\", \"true\"))) {\n                    Path archiveRootPath = archiveFs.getPath(\"/\");\n                    SourceSaver saver = api.getSourceSaver(entry);\n\n                    if (saver != null) {\n                        saver.saveContent(\n                            api,\n                            () -> controller.isCancelled(),\n                            (p) -> listener. pathSaved(p),\n                            archiveRootPath, archiveRootPath, entry);\n                    }\n                }\n            } catch (URISyntaxException|IOException e) {\n                assert ExceptionUtil.printStackTrace(e);\n            }\n        }\n    }\n\n    protected static class DelegatedMap<K, V> implements Map<K, V> {\n        protected Map<K, V> map;\n\n        public DelegatedMap(Map<K, V> map) { this.map = map; }\n\n        @Override public int size() { return map.size(); }\n        @Override public boolean isEmpty() { return map.isEmpty(); }\n        @Override public boolean containsKey(Object o) { return map.containsKey(o); }\n        @Override public boolean containsValue(Object o) { return map.containsValue(o); }\n        @Override public V get(Object o) { return map.get(o); }\n        @Override public V put(K k, V v) { return map.put(k, v); }\n        @Override public V remove(Object o) { return map.remove(o); }\n        @Override public void putAll(Map<? extends K, ? extends V> map) { this.map.putAll(map); }\n        @Override public void clear() { map.clear(); }\n        @Override public Set<K> keySet() { return map.keySet(); }\n        @Override public Collection<V> values() { return map.values(); }\n        @Override public Set<Entry<K, V>> entrySet() { return map.entrySet(); }\n        @Override public boolean equals(Object o) { return map.equals(o); }\n        @Override public int hashCode() { return map.hashCode(); }\n    }\n\n    protected static class DelegatedMapWithDefault extends DelegatedMap<String, Collection> {\n        public DelegatedMapWithDefault(Map<String, Collection> map) { super(map); }\n\n        @Override public Collection get(Object o) {\n            Collection value = map.get(o);\n            if (value == null) {\n                String key = o.toString();\n                map.put(key, value=new ArrayList());\n            }\n            return value;\n        }\n    }\n\n    protected static class DelegatedMapMapWithDefault extends DelegatedMap<String, Map<String, Collection>> {\n\t    protected HashMap<String, Map<String, Collection>> wrappers = new HashMap<>();\n\n        public DelegatedMapMapWithDefault(Map<String, Map<String, Collection>> map) { super(map); }\n\n        @Override public Map<String, Collection> get(Object o) {\n            Map<String, Collection> value = wrappers.get(o);\n\n            if (value == null) {\n                String key = o.toString();\n                HashMap<String, Collection> m = new HashMap<>();\n                map.put(key, m);\n                wrappers.put(key, value=new DelegatedMapWithDefault(m));\n            }\n\n            return value;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/service/mainpanel/PanelFactoryService.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.mainpanel;\n\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.service.extension.ExtensionService;\nimport org.jd.gui.spi.PanelFactory;\n\nimport java.util.Collection;\nimport java.util.HashMap;\n\npublic class PanelFactoryService {\n    protected static final PanelFactoryService PANEL_FACTORY_SERVICE = new PanelFactoryService();\n\n    public static PanelFactoryService getInstance() { return PANEL_FACTORY_SERVICE; }\n\n    protected HashMap<String, PanelFactory> mapProviders = new HashMap<>();\n\n    protected PanelFactoryService() {\n        Collection<PanelFactory> providers = ExtensionService.getInstance().load(PanelFactory.class);\n\n        for (PanelFactory provider : providers) {\n            for (String type : provider.getTypes()) {\n                mapProviders.put(type, provider);\n            }\n        }\n    }\n\n    public PanelFactory get(Container container) {\n        PanelFactory factory = mapProviders.get(container.getType());\n        return (factory != null) ? factory : mapProviders.get(\"default\");\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/service/pastehandler/PasteHandlerService.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.pastehandler;\n\nimport org.jd.gui.service.extension.ExtensionService;\nimport org.jd.gui.spi.PasteHandler;\n\nimport java.util.Collection;\n\npublic class PasteHandlerService {\n    protected static final PasteHandlerService PASTE_HANDLER_SERVICE = new PasteHandlerService();\n\n    public static PasteHandlerService getInstance() { return PASTE_HANDLER_SERVICE; }\n\n    protected final Collection<PasteHandler> providers = ExtensionService.getInstance().load(PasteHandler.class);\n\n    public PasteHandler get(Object obj) {\n        for (PasteHandler provider : providers) {\n            if (provider.accept(obj)) {\n                return provider;\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/service/platform/PlatformService.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.platform;\n\npublic class PlatformService {\n\tprotected static final PlatformService PLATFORM_SERVICE = new PlatformService();\n\n\tpublic enum OS { Linux, MacOSX, Windows }\n\n\tprotected OS os;\n\n\tprotected PlatformService() {\n\t\tString osName = System.getProperty(\"os.name\").toLowerCase();\n\n\t\tif (osName.contains(\"windows\")) {\n\t\t\tos = OS.Windows;\n\t\t} else if (osName.contains(\"mac os\")) {\n\t\t\tos = OS.MacOSX;\n\t\t} else {\n\t\t\tos = OS.Linux;\n\t\t}\n\t}\n\n\tpublic static PlatformService getInstance() { return PLATFORM_SERVICE; }\n\n\tpublic OS getOs() { return os; }\n\n\tpublic boolean isLinux() { return os == OS.Linux; }\n\tpublic boolean isMac() { return os == OS.MacOSX; }\n\tpublic boolean isWindows() { return os == OS.Windows; }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/service/preferencespanel/PreferencesPanelService.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.preferencespanel;\n\nimport org.jd.gui.service.extension.ExtensionService;\nimport org.jd.gui.spi.PreferencesPanel;\n\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Iterator;\n\npublic class PreferencesPanelService {\n    protected static final PreferencesPanelService PREFERENCES_PANEL_SERVICE = new PreferencesPanelService();\n\n    public static PreferencesPanelService getInstance() { return PREFERENCES_PANEL_SERVICE; }\n\n    protected final Collection<PreferencesPanel> providers;\n\n    protected PreferencesPanelService() {\n        Collection<PreferencesPanel> list = ExtensionService.getInstance().load(PreferencesPanel.class);\n        Iterator<PreferencesPanel> iterator = list.iterator();\n\n        while (iterator.hasNext()) {\n            if (!iterator.next().isActivated()) {\n                iterator.remove();\n            }\n        }\n\n        HashMap<String, PreferencesPanel> map = new HashMap<>();\n\n        for (PreferencesPanel panel : list) {\n            map.put(panel.getPreferencesGroupTitle() + '$' + panel.getPreferencesPanelTitle(), panel);\n        }\n\n        providers = map.values();\n    }\n\n    public Collection<PreferencesPanel> getProviders() {\n        return providers;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/service/preferencespanel/UISingleInstancePreferencesProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.preferencespanel;\n\nimport org.jd.gui.service.platform.PlatformService;\nimport org.jd.gui.spi.PreferencesPanel;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.util.Map;\n\n/**\n * Single instance is the default mode on Mac OSX, so this panel is not activated.\n */\npublic class UISingleInstancePreferencesProvider extends JPanel implements PreferencesPanel {\n    protected static final String SINGLE_INSTANCE = \"UIMainWindowPreferencesProvider.singleInstance\";\n\n    protected JCheckBox singleInstanceTabsCheckBox;\n\n    public UISingleInstancePreferencesProvider() {\n        super(new GridLayout(0,1));\n\n        singleInstanceTabsCheckBox = new JCheckBox(\"Single instance\");\n\n        add(singleInstanceTabsCheckBox);\n    }\n\n    // --- PreferencesPanel --- //\n    @Override public String getPreferencesGroupTitle() { return \"User Interface\"; }\n    @Override public String getPreferencesPanelTitle() { return \"Main window\"; }\n    @Override public JComponent getPanel() { return this; }\n\n    @Override public void init(Color errorBackgroundColor) {}\n\n    @Override public boolean isActivated() { return !PlatformService.getInstance().isMac(); }\n\n    @Override\n    public void loadPreferences(Map<String, String> preferences) {\n        singleInstanceTabsCheckBox.setSelected(\"true\".equals(preferences.get(SINGLE_INSTANCE)));\n    }\n\n    @Override\n    public void savePreferences(Map<String, String> preferences) {\n        preferences.put(SINGLE_INSTANCE, Boolean.toString(singleInstanceTabsCheckBox.isSelected()));\n    }\n\n    @Override public boolean arePreferencesValid() { return true; }\n\n    @Override public void addPreferencesChangeListener(PreferencesPanel.PreferencesPanelChangeListener listener) {}\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/service/preferencespanel/UITabsPreferencesProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.preferencespanel;\n\nimport org.jd.gui.service.platform.PlatformService;\nimport org.jd.gui.spi.PreferencesPanel;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.util.Map;\n\n/**\n * JTabbedPane.WRAP_TAB_LAYOUT is not supported by Aqua L&F.\n * This panel is not activated on Mac OSX.\n */\npublic class UITabsPreferencesProvider extends JPanel implements PreferencesPanel {\n    protected static final String TAB_LAYOUT = \"UITabsPreferencesProvider.singleLineTabs\";\n\n    protected JCheckBox singleLineTabsCheckBox;\n\n    public UITabsPreferencesProvider() {\n        super(new GridLayout(0,1));\n\n        singleLineTabsCheckBox = new JCheckBox(\"Tabs on a single line\");\n\n        add(singleLineTabsCheckBox);\n    }\n\n    // --- PreferencesPanel --- //\n    @Override public String getPreferencesGroupTitle() { return \"User Interface\"; }\n    @Override public String getPreferencesPanelTitle() { return \"Tabs\"; }\n    @Override public JComponent getPanel() { return this; }\n\n    @Override public void init(Color errorBackgroundColor) {}\n\n    @Override public boolean isActivated() { return !PlatformService.getInstance().isMac(); }\n\n    @Override public void loadPreferences(Map<String, String> preferences) {\n        singleLineTabsCheckBox.setSelected(\"true\".equals(preferences.get(TAB_LAYOUT)));\n    }\n\n    @Override public void savePreferences(Map<String, String> preferences) {\n        preferences.put(TAB_LAYOUT, Boolean.toString(singleLineTabsCheckBox.isSelected()));\n    }\n\n    @Override public boolean arePreferencesValid() { return true; }\n\n    @Override public void addPreferencesChangeListener(PreferencesPanel.PreferencesPanelChangeListener listener) {}\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/service/sourceloader/SourceLoaderService.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.sourceloader;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.service.extension.ExtensionService;\nimport org.jd.gui.spi.SourceLoader;\n\nimport java.io.File;\nimport java.util.Collection;\n\npublic class SourceLoaderService {\n    protected static final SourceLoaderService SOURCE_LOADER_SERVICE = new SourceLoaderService();\n\n    public static SourceLoaderService getInstance() { return SOURCE_LOADER_SERVICE; }\n\n    protected Collection<SourceLoader> providers = ExtensionService.getInstance().load(SourceLoader.class);\n\n    public String getSource(API api, Container.Entry entry) {\n        for (SourceLoader provider : providers) {\n            String source = provider.getSource(api, entry);\n\n            if ((source != null) && !source.isEmpty()) {\n                return source;\n            }\n        }\n\n        return null;\n    }\n\n    public String loadSource(API api, Container.Entry entry) {\n        for (SourceLoader provider : providers) {\n            String source = provider.loadSource(api, entry);\n\n            if ((source != null) && !source.isEmpty()) {\n                return source;\n            }\n        }\n\n        return null;\n    }\n\n    public File getSourceFile(API api, Container.Entry entry) {\n        for (SourceLoader provider : providers) {\n            File file = provider.loadSourceFile(api, entry);\n\n            if (file != null) {\n                return file;\n            }\n        }\n\n        return null;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/service/sourcesaver/SourceSaverService.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.sourcesaver;\n\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.service.extension.ExtensionService;\nimport org.jd.gui.spi.SourceSaver;\n\nimport java.util.Collection;\nimport java.util.HashMap;\n\npublic class SourceSaverService {\n    protected static final SourceSaverService SOURCE_SAVER_SERVICE = new SourceSaverService();\n\n    public static SourceSaverService getInstance() { return SOURCE_SAVER_SERVICE; }\n\n    protected HashMap<String, SourceSavers> mapProviders = new HashMap<>();\n\n    protected SourceSaverService() {\n        Collection<SourceSaver> providers = ExtensionService.getInstance().load(SourceSaver.class);\n\n        for (SourceSaver provider : providers) {\n            for (String selector : provider.getSelectors()) {\n                SourceSavers savers = mapProviders.get(selector);\n\n                if (savers == null) {\n                    mapProviders.put(selector, savers=new SourceSavers());\n                }\n\n                savers.add(provider);\n            }\n        }\n    }\n\n    public SourceSaver get(Container.Entry entry) {\n        SourceSaver saver = get(entry.getContainer().getType(), entry);\n        return (saver != null) ? saver : get(\"*\", entry);\n    }\n\n    protected SourceSaver get(String containerType, Container.Entry entry) {\n        String path = entry.getPath();\n        String type = entry.isDirectory() ? \"dir\" : \"file\";\n        String prefix = containerType + ':' + type;\n        SourceSaver saver = null;\n        SourceSavers savers = mapProviders.get(prefix + ':' + path);\n\n        if (savers != null) {\n            saver = savers.match(path);\n        }\n\n        if (saver == null) {\n            int lastSlashIndex = path.lastIndexOf('/');\n            String name = path.substring(lastSlashIndex+1);\n\n            savers = mapProviders.get(prefix + \":*/\" + path);\n            if (savers != null) {\n                saver = savers.match(path);\n            }\n\n            if (saver == null) {\n                int index = name.lastIndexOf('.');\n\n                if (index != -1) {\n                    String extension = name.substring(index + 1);\n\n                    savers = mapProviders.get(prefix + \":*.\" + extension);\n                    if (savers != null) {\n                        saver = savers.match(path);\n                    }\n                }\n\n                if (saver == null) {\n                    savers = mapProviders.get(prefix + \":*\");\n                    if (savers != null) {\n                        saver = savers.match(path);\n                    }\n                }\n            }\n        }\n\n        return saver;\n    }\n\n    protected static class SourceSavers {\n        protected HashMap<String, SourceSaver> savers = new HashMap<>();\n        protected SourceSaver defaultSaver;\n\n        void add(SourceSaver saver) {\n            if (saver.getPathPattern() != null) {\n                savers.put(saver.getPathPattern().pattern(), saver);\n            } else {\n                defaultSaver = saver;\n            }\n        }\n\n        SourceSaver match(String path) {\n            for (SourceSaver saver : savers.values()) {\n                if (saver.getPathPattern().matcher(path).matches()) {\n                    return saver;\n                }\n            }\n            return defaultSaver;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/service/treenode/TreeNodeFactoryService.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.service.extension.ExtensionService;\nimport org.jd.gui.spi.TreeNodeFactory;\n\nimport java.util.Collection;\nimport java.util.HashMap;\n\npublic class TreeNodeFactoryService {\n    protected static final TreeNodeFactoryService TREE_NODE_FACTORY_SERVICE = new TreeNodeFactoryService();\n\n    public static TreeNodeFactoryService getInstance() { return TREE_NODE_FACTORY_SERVICE; }\n\n    protected HashMap<String, TreeNodeFactories> mapProviders = new HashMap<>();\n\n    protected TreeNodeFactoryService() {\n        Collection<TreeNodeFactory> providers = ExtensionService.getInstance().load(TreeNodeFactory.class);\n\n        for (TreeNodeFactory provider : providers) {\n            for (String selector : provider.getSelectors()) {\n                TreeNodeFactories factories = mapProviders.get(selector);\n\n                if (factories == null) {\n                    mapProviders.put(selector, factories=new TreeNodeFactories());\n                }\n\n                factories.add(provider);\n            }\n        }\n    }\n\n    public TreeNodeFactory get(Container.Entry entry) {\n        TreeNodeFactory factory = get(entry.getContainer().getType(), entry);\n        return (factory != null) ? factory : get(\"*\", entry);\n    }\n\n    protected TreeNodeFactory get(String containerType, Container.Entry entry) {\n        String path = entry.getPath();\n        String type = entry.isDirectory() ? \"dir\" : \"file\";\n        String prefix = containerType + ':' + type + ':';\n        TreeNodeFactory factory = null;\n        TreeNodeFactories factories = mapProviders.get(prefix + path);\n\n        if (factories != null) {\n            factory = factories.match(path);\n        }\n\n        if (factory == null) {\n            int lastSlashIndex = path.lastIndexOf('/');\n            String name = path.substring(lastSlashIndex+1);\n\n            factories = mapProviders.get(prefix + \"*/\" + name);\n            if (factories != null) {\n                factory = factories.match(path);\n            }\n\n            if (factory == null) {\n                int index = name.lastIndexOf('.');\n\n                if (index != -1) {\n                    String extension = name.substring(index + 1);\n\n                    factories = mapProviders.get(prefix + \"*.\" + extension);\n                    if (factories != null) {\n                        factory = factories.match(path);\n                    }\n                }\n\n                if (factory == null) {\n                    factories = mapProviders.get(prefix + \"*\");\n                    if (factories != null) {\n                        factory = factories.match(path);\n                    }\n                }\n            }\n        }\n\n        return factory;\n    }\n\n    protected static class TreeNodeFactories {\n        protected HashMap<String, TreeNodeFactory> factories = new HashMap<>();\n        protected TreeNodeFactory defaultFactory;\n\n        public void add(TreeNodeFactory factory) {\n            if (factory.getPathPattern() != null) {\n                factories.put(factory.getPathPattern().pattern(), factory);\n            } else {\n                defaultFactory = factory;\n            }\n        }\n\n        public TreeNodeFactory match(String path) {\n            for (TreeNodeFactory factory : factories.values()) {\n                if (factory.getPathPattern().matcher(path).matches()) {\n                    return factory;\n                }\n            }\n            return defaultFactory;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/service/type/TypeFactoryService.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.type;\n\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.service.extension.ExtensionService;\nimport org.jd.gui.spi.TypeFactory;\n\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\npublic class TypeFactoryService {\n    protected static final TypeFactoryService TYPE_FACTORY_SERVICE = new TypeFactoryService();\n\n    protected Map<String, TypeFactories> mapProviders;\n\n    public static TypeFactoryService getInstance() {\n        return TYPE_FACTORY_SERVICE;\n    }\n\n    protected TypeFactoryService() {\n        Collection<TypeFactory> providers = ExtensionService.getInstance().load(TypeFactory.class);\n\n        mapProviders = new HashMap<>();\n\n        for (TypeFactory provider : providers) {\n            for (String selector : provider.getSelectors()) {\n                TypeFactories typeFactories = mapProviders.get(selector);\n\n                if (typeFactories == null) {\n                    mapProviders.put(selector, typeFactories=new TypeFactories());\n                }\n\n                typeFactories.add(provider);\n            }\n        }\n    }\n\n    public TypeFactory get(Container.Entry entry) {\n        TypeFactory typeFactory = get(entry.getContainer().getType(), entry);\n        return (typeFactory != null) ? typeFactory : get(\"*\", entry);\n    }\n\n    public TypeFactory get(String containerType, Container.Entry entry) {\n        String path = entry.getPath();\n        String type = entry.isDirectory() ? \"dir\" : \"file\";\n        String prefix = containerType + ':' + type + ':';\n        TypeFactories typeFactories = mapProviders.get(prefix + path);\n        TypeFactory factory = null;\n\n        if (typeFactories != null) {\n            factory = typeFactories.match(path);\n        }\n\n        if (factory == null) {\n            int lastSlashIndex = path.lastIndexOf('/');\n            String name = path.substring(lastSlashIndex+1);\n\n            typeFactories = mapProviders.get(prefix + \"*/\" + name);\n\n            if (typeFactories != null) {\n                factory = typeFactories.match(path);\n            }\n\n            if (factory == null) {\n                int index = name.lastIndexOf('.');\n                if (index != -1) {\n                    String extension = name.substring(index + 1);\n\n                    typeFactories = mapProviders.get(prefix + \"*.\" + extension);\n\n                    if (typeFactories != null) {\n                        factory = typeFactories.match(path);\n                    }\n                }\n                if (factory == null) {\n                    typeFactories = mapProviders.get(prefix + '*');\n\n                    if (typeFactories != null) {\n                        factory = typeFactories.match(path);\n                    }\n                }\n            }\n        }\n\n        return factory;\n    }\n\n    protected static class TypeFactories {\n        protected HashMap<String, TypeFactory> factories = new HashMap<>();\n        protected TypeFactory defaultFactory;\n\n        public void add(TypeFactory factory) {\n            Pattern pathPattern = factory.getPathPattern();\n\n            if (pathPattern != null) {\n                factories.put(pathPattern.pattern(), factory);\n            } else {\n                defaultFactory = factory;\n            }\n        }\n\n        public TypeFactory match(String path) {\n            for (TypeFactory factory : factories.values()) {\n                Matcher matcher = factory.getPathPattern().matcher(path);\n\n                if (matcher.matches()) {\n                    return factory;\n                }\n            }\n            return defaultFactory;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/service/uriloader/UriLoaderService.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.uriloader;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.service.extension.ExtensionService;\nimport org.jd.gui.spi.UriLoader;\n\nimport java.net.URI;\nimport java.util.Collection;\nimport java.util.HashMap;\n\npublic class UriLoaderService {\n    protected static final UriLoaderService URI_LOADER_SERVICE = new UriLoaderService();\n\n    public static UriLoaderService getInstance() { return URI_LOADER_SERVICE; }\n\n    protected HashMap<String, UriLoader> mapProviders = new HashMap<>();\n\n    protected UriLoaderService() {\n        Collection<UriLoader> providers = ExtensionService.getInstance().load(UriLoader.class);\n\n        for (UriLoader provider : providers) {\n            for (String scheme : provider.getSchemes()) {\n                mapProviders.put(scheme, provider);\n            }\n        }\n    }\n\n    public UriLoader get(API api, URI uri) {\n        UriLoader provider = mapProviders.get(uri.getScheme());\n\n        if (provider.accept(api, uri)) {\n            return provider;\n        } else {\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/util/exception/ExceptionUtil.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.util.exception;\n\npublic class ExceptionUtil {\n    public static boolean printStackTrace(Throwable throwable) {\n        throwable.printStackTrace();\n        return true;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/util/function/TriConsumer.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.util.function;\n\nimport java.util.Objects;\n\n@FunctionalInterface\npublic interface TriConsumer<T, U, V> {\n    void accept(T t, U u, V v);\n\n    default TriConsumer<T, U, V> andThen(TriConsumer<? super T, ? super U, ? super V> after) {\n        Objects.requireNonNull(after);\n\n        return (a, b, c) -> {\n            accept(a, b, c);\n            after.accept(a, b, c);\n        };\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/util/net/InterProcessCommunicationUtil.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.util.net;\n\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport java.io.IOException;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.net.InetAddress;\nimport java.net.ServerSocket;\nimport java.net.Socket;\nimport java.util.function.Consumer;\n\npublic class InterProcessCommunicationUtil {\n    protected static final int PORT = 2015_6;\n\n    public static void listen(final Consumer<String[]> consumer) throws Exception {\n        final ServerSocket listener = new ServerSocket(PORT);\n\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                while (true) {\n                    try (Socket socket = listener.accept();\n                         ObjectInputStream ois = new ObjectInputStream(socket.getInputStream())) {\n                        // Receive args from another JD-GUI instance\n                        String[] args = (String[])ois.readObject();\n                        consumer.accept(args);\n                    } catch (IOException|ClassNotFoundException e) {\n                        assert ExceptionUtil.printStackTrace(e);\n                    }\n                }\n            }\n        };\n\n        new Thread(runnable).start();\n    }\n\n    public static void send(String[] args) {\n        try (Socket socket = new Socket(InetAddress.getLocalHost(), PORT);\n             ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream())) {\n            // Send args to the main JD-GUI instance\n            oos.writeObject(args);\n        } catch (IOException e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/util/net/UriUtil.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.util.net;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.api.model.Type;\nimport org.jd.gui.service.type.TypeFactoryService;\nimport org.jd.gui.spi.TypeFactory;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.Collection;\nimport java.util.concurrent.Future;\n\npublic class UriUtil {\n    /*\n     * Convert inner entry URI to outer entry uri with a fragment. Example:\n     *  file://codebase/a/b/c/D$E.class => file://codebase/a/b/c/D.class#typeDeclaration=D$E\n     */\n    public static URI createURI(API api, Collection<Future<Indexes>> collectionOfFutureIndexes, Container.Entry entry, String query, String fragment) {\n        URI uri = entry.getUri();\n\n        try {\n            String path = uri.getPath();\n            TypeFactory typeFactory = TypeFactoryService.getInstance().get(entry);\n\n            if (typeFactory != null) {\n                Type type = typeFactory.make(api, entry, fragment);\n\n                if (type != null) {\n                    path = getOuterPath(collectionOfFutureIndexes, entry, type);\n                }\n            }\n\n            return new URI(uri.getScheme(), uri.getHost(), path, query, fragment);\n        } catch (URISyntaxException e) {\n            assert ExceptionUtil.printStackTrace(e);\n            return uri;\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected static String getOuterPath(Collection<Future<Indexes>> collectionOfFutureIndexes, Container.Entry entry, Type type) {\n        String outerName = type.getOuterName();\n\n        if (outerName != null) {\n            try {\n                for (Future<Indexes> futureIndexes : collectionOfFutureIndexes) {\n                    if (futureIndexes.isDone()) {\n                        Collection<Container.Entry> outerEntries = futureIndexes.get().getIndex(\"typeDeclarations\").get(outerName);\n\n                        if (outerEntries != null) {\n                            for (Container.Entry outerEntry : outerEntries) {\n                                if (outerEntry.getContainer() == entry.getContainer()) {\n                                    return outerEntry.getUri().getPath();\n                                }\n                            }\n                        }\n                    }\n                }\n            } catch (Exception e) {\n                assert ExceptionUtil.printStackTrace(e);\n            }\n        }\n\n        return entry.getUri().getPath();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/util/swing/SwingUtil.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.util.swing;\n\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\n\n/**\n * See: https://www.ailis.de/~k/archives/67-Workaround-for-borderless-Java-Swing-menus-on-Linux.html\n */\npublic class SwingUtil {\n    /*\n     * This is free and unencumbered software released into the public domain.\n     *\n     * Anyone is free to copy, modify, publish, use, compile, sell, or\n     * distribute this software, either in source code form or as a compiled\n     * binary, for any purpose, commercial or non-commercial, and by any\n     * means.\n     *\n     * In jurisdictions that recognize copyright laws, the author or authors\n     * of this software dedicate any and all copyright interest in the\n     * software to the public domain. We make this dedication for the benefit\n     * of the public at large and to the detriment of our heirs and\n     * successors. We intend this dedication to be an overt act of\n     * relinquishment in perpetuity of all present and future rights to this\n     * software under copyright law.\n     *\n     * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n     * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n     * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n     * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\n     * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\n     * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n     * OTHER DEALINGS IN THE SOFTWARE.\n     *\n     * For more information, please refer to <http://unlicense.org/>\n     */\n\n    /**\n     * Swing menus are looking pretty bad on Linux when the GTK LaF is used (See\n     * bug #6925412). It will most likely never be fixed anytime soon so this\n     * method provides a workaround for it. It uses reflection to change the GTK\n     * style objects of Swing so popup menu borders have a minimum thickness of\n     * 1 and menu separators have a minimum vertical thickness of 1.\n     */\n    public static void installGtkPopupBugWorkaround() {\n        // Get current look-and-feel implementation class\n        LookAndFeel laf = UIManager.getLookAndFeel();\n        Class<?> lafClass = laf.getClass();\n\n        // Do nothing when not using the problematic LaF\n        if (!lafClass.getName().equals(\"com.sun.java.swing.plaf.gtk.GTKLookAndFeel\")) return;\n\n        // We do reflection from here on. Failure is silently ignored. The\n        // workaround is simply not installed when something goes wrong here\n        try {\n            // Access the GTK style factory\n            Field field = lafClass.getDeclaredField(\"styleFactory\");\n            boolean accessible = field.isAccessible();\n            field.setAccessible(true);\n            Object styleFactory = field.get(laf);\n            field.setAccessible(accessible);\n\n            // Fix the horizontal and vertical thickness of popup menu style\n            Object style = getGtkStyle(styleFactory, new JPopupMenu(), \"POPUP_MENU\");\n            fixGtkThickness(style, \"yThickness\");\n            fixGtkThickness(style, \"xThickness\");\n\n            // Fix the vertical thickness of the popup menu separator style\n            style = getGtkStyle(styleFactory, new JSeparator(), \"POPUP_MENU_SEPARATOR\");\n            fixGtkThickness(style, \"yThickness\");\n        } catch (Exception e) {\n            // Silently ignored. Workaround can't be applied.\n            assert ExceptionUtil.printStackTrace(e);\n        }\n    }\n\n    /**\n     * Called internally by installGtkPopupBugWorkaround to fix the thickness\n     * of a GTK style field by setting it to a minimum value of 1.\n     *\n     * @param style\n     *            The GTK style object.\n     * @param fieldName\n     *            The field name.\n     * @throws Exception\n     *             When reflection fails.\n     */\n    private static void fixGtkThickness(Object style, String fieldName) throws Exception {\n        Field field = style.getClass().getDeclaredField(fieldName);\n        boolean accessible = field.isAccessible();\n        field.setAccessible(true);\n        field.setInt(style, Math.max(1, field.getInt(style)));\n        field.setAccessible(accessible);\n    }\n\n    /**\n     * Called internally by installGtkPopupBugWorkaround. Returns a specific\n     * GTK style object.\n     *\n     * @param styleFactory\n     *            The GTK style factory.\n     * @param component\n     *            The target component of the style.\n     * @param regionName\n     *            The name of the target region of the style.\n     * @return The GTK style.\n     * @throws Exception\n     *             When reflection fails.\n     */\n    private static Object getGtkStyle(Object styleFactory, JComponent component, String regionName) throws Exception {\n        // Create the region object\n        Class<?> regionClass = Class.forName(\"javax.swing.plaf.synth.Region\");\n        Field field = regionClass.getField(regionName);\n        Object region = field.get(regionClass);\n\n        // Get and return the style\n        Class<?> styleFactoryClass = styleFactory.getClass();\n        Method method = styleFactoryClass.getMethod(\"getStyle\", JComponent.class, regionClass);\n        boolean accessible = method.isAccessible();\n        method.setAccessible(true);\n        Object style = method.invoke(styleFactory, component, region);\n        method.setAccessible(accessible);\n        return style;\n    }\n\n    public static void invokeLater(Runnable runnable) {\n        if (SwingUtilities.isEventDispatchThread()) {\n            runnable.run();\n        } else {\n            SwingUtilities.invokeLater(runnable);\n        }\n    }\n\n    public static Image getImage(String iconPath) {\n        return Toolkit.getDefaultToolkit().getImage(SwingUtil.class.getResource(iconPath));\n    }\n\n    public static ImageIcon newImageIcon(String iconPath) {\n        return new ImageIcon(getImage(iconPath));\n    }\n\n    public static Action newAction(String name, boolean enable, ActionListener listener) {\n        Action action = new AbstractAction(name) {\n            @Override\n            public void actionPerformed(ActionEvent actionEvent) {\n                listener.actionPerformed(actionEvent);\n            }\n        };\n        action.setEnabled(enable);\n        return action;\n    }\n\n    public static Action newAction(String name, ImageIcon icon, boolean enable, ActionListener listener) {\n        Action action = newAction(name, enable, listener);\n        action.putValue(Action.SMALL_ICON, icon);\n        return action;\n    }\n\n    public static Action newAction(ImageIcon icon, boolean enable, ActionListener listener) {\n        Action action = newAction(null, icon, enable, listener);\n        action.putValue(Action.SMALL_ICON, icon);\n        return action;\n    }\n\n    public static Action newAction(String name, ImageIcon icon, boolean enable, String shortDescription, ActionListener listener) {\n        Action action = newAction(name, icon, enable, listener);\n        action.putValue(Action.SHORT_DESCRIPTION, shortDescription);\n        return action;\n    }\n\n    public static Action newAction(String name, boolean enable, String shortDescription, ActionListener listener) {\n        Action action = newAction(name, enable, listener);\n        action.putValue(Action.SHORT_DESCRIPTION, shortDescription);\n        return action;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/view/AboutView.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view;\n\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.util.swing.SwingUtil;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.KeyEvent;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URL;\nimport java.util.Enumeration;\nimport java.util.jar.Attributes;\nimport java.util.jar.Manifest;\n\npublic class AboutView {\n    protected JDialog aboutDialog;\n    protected JButton aboutOkButton;\n\n    public AboutView(JFrame mainFrame) {\n        // Build GUI\n        SwingUtil.invokeLater(() -> {\n            aboutDialog = new JDialog(mainFrame, \"About Java Decompiler\", false);\n            aboutDialog.setResizable(false);\n\n            JPanel panel = new JPanel();\n            panel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));\n            panel.setLayout(new BorderLayout());\n            aboutDialog.add(panel);\n\n            Box vbox = Box.createVerticalBox();\n            panel.add(vbox, BorderLayout.NORTH);\n            JPanel subpanel = new JPanel();\n            vbox.add(subpanel);\n            subpanel.setBorder(BorderFactory.createLineBorder(Color.BLACK));\n            subpanel.setBackground(Color.WHITE);\n            subpanel.setLayout(new BorderLayout());\n            JLabel logo = new JLabel(new ImageIcon(SwingUtil.getImage(\"/org/jd/gui/images/jd_icon_64.png\")));\n            logo.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));\n            subpanel.add(logo, BorderLayout.WEST);\n            Box subvbox = Box.createVerticalBox();\n            subvbox.setBorder(BorderFactory.createEmptyBorder(15,0,15,15));\n            subpanel.add(subvbox, BorderLayout.EAST);\n            Box hbox = Box.createHorizontalBox();\n            subvbox.add(hbox);\n            JLabel mainLabel = new JLabel(\"Java Decompiler\");\n            mainLabel.setFont(UIManager.getFont(\"Label.font\").deriveFont(Font.BOLD, 14));\n            hbox.add(mainLabel);\n            hbox.add(Box.createHorizontalGlue());\n            hbox = Box.createHorizontalBox();\n            subvbox.add(hbox);\n            JPanel subsubpanel = new JPanel();\n            hbox.add(subsubpanel);\n            subsubpanel.setLayout(new GridLayout(2,2));\n            subsubpanel.setOpaque(false);\n            subsubpanel.setBorder(BorderFactory.createEmptyBorder(5,10,5,5));\n\n            String jdGuiVersion = \"SNAPSHOT\";\n            String jdCoreVersion = \"SNAPSHOT\";\n\n            try {\n                Enumeration<URL> enumeration = AboutView.class.getClassLoader().getResources(\"META-INF/MANIFEST.MF\");\n\n                while (enumeration.hasMoreElements()) {\n                    try (InputStream is = enumeration.nextElement().openStream()) {\n                        Attributes attributes = new Manifest(is).getMainAttributes();\n                        String attribute = attributes.getValue(\"JD-GUI-Version\");\n\n                        if (attribute != null) {\n                            jdGuiVersion = attribute;\n                        }\n\n                        attribute = attributes.getValue(\"JD-Core-Version\");\n\n                        if (attribute != null) {\n                            jdCoreVersion = attribute;\n                        }\n                    }\n                }\n            } catch (IOException e) {\n                assert ExceptionUtil.printStackTrace(e);\n            }\n\n            subsubpanel.add(new JLabel(\"JD-GUI\"));\n            subsubpanel.add(new JLabel(\"version \" + jdGuiVersion));\n            subsubpanel.add(new JLabel(\"JD-Core\"));\n            subsubpanel.add(new JLabel(\"version \" + jdCoreVersion));\n\n            hbox.add(Box.createHorizontalGlue());\n\n            hbox = Box.createHorizontalBox();\n            hbox.add(new JLabel(\"Copyright © 2008, 2019 Emmanuel Dupuy\"));\n            hbox.add(Box.createHorizontalGlue());\n            subvbox.add(hbox);\n\n            vbox.add(Box.createVerticalStrut(10));\n\n            hbox = Box.createHorizontalBox();\n            panel.add(hbox, BorderLayout.SOUTH);\n            hbox.add(Box.createHorizontalGlue());\n            aboutOkButton = new JButton(\"    Ok    \");\n            Action aboutOkActionListener = new AbstractAction() {\n                @Override public void actionPerformed(ActionEvent actionEvent) { aboutDialog.setVisible(false); }\n            };\n            aboutOkButton.addActionListener(aboutOkActionListener);\n            hbox.add(aboutOkButton);\n            hbox.add(Box.createHorizontalGlue());\n\n            // Last setup\n            JRootPane rootPane = aboutDialog.getRootPane();\n            rootPane.setDefaultButton(aboutOkButton);\n            rootPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), \"AboutView.ok\");\n            rootPane.getActionMap().put(\"AboutView.ok\", aboutOkActionListener);\n\n            // Prepare to display\n            aboutDialog.pack();\n        });\n    }\n\n    public void show() {\n        SwingUtil.invokeLater(() -> {\n            // Show\n            aboutDialog.setLocationRelativeTo(aboutDialog.getParent());\n            aboutDialog.setVisible(true);\n            aboutOkButton.requestFocus();\n        });\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/view/GoToView.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view;\n\nimport org.jd.gui.api.feature.LineNumberNavigable;\nimport org.jd.gui.model.configuration.Configuration;\nimport org.jd.gui.util.swing.SwingUtil;\n\nimport javax.swing.*;\nimport javax.swing.event.DocumentEvent;\nimport javax.swing.event.DocumentListener;\nimport java.awt.*;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.KeyAdapter;\nimport java.awt.event.KeyEvent;\nimport java.util.function.IntConsumer;\n\npublic class GoToView {\n    protected JDialog goToDialog;\n    protected JLabel goToEnterLineNumberLabel;\n    protected JTextField goToEnterLineNumberTextField;\n    protected JLabel goToEnterLineNumberErrorLabel;\n\n    protected LineNumberNavigable navigator;\n    protected IntConsumer okCallback;\n\n    public GoToView(Configuration configuration, JFrame mainFrame) {\n        // Build GUI\n        SwingUtil.invokeLater(() -> {\n            goToDialog = new JDialog(mainFrame, \"Go to Line\", false);\n            goToDialog.setResizable(false);\n\n            Box vbox = Box.createVerticalBox();\n            vbox.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));\n            goToDialog.add(vbox);\n\n            // First label \"Enter line number (1..xxx):\"\n            Box hbox = Box.createHorizontalBox();\n            hbox.add(goToEnterLineNumberLabel = new JLabel());\n            hbox.add(Box.createHorizontalGlue());\n            vbox.add(hbox);\n\n            vbox.add(Box.createVerticalStrut(10));\n\n            // Text field\n            vbox.add(goToEnterLineNumberTextField = new JTextField(30));\n\n            vbox.add(Box.createVerticalStrut(10));\n\n            // Error label\n            hbox = Box.createHorizontalBox();\n            hbox.add(goToEnterLineNumberErrorLabel = new JLabel(\" \"));\n            goToEnterLineNumberTextField.addKeyListener(new KeyAdapter() {\n                @Override public void keyTyped(KeyEvent e) {\n                    if (! Character.isDigit(e.getKeyChar())) {\n                        e.consume();\n                    }\n                }\n            });\n            hbox.add(Box.createHorizontalGlue());\n            vbox.add(hbox);\n\n            vbox.add(Box.createVerticalStrut(15));\n\n            // Buttons \"Ok\" and \"Cancel\"\n            hbox = Box.createHorizontalBox();\n            hbox.add(Box.createHorizontalGlue());\n            JButton goToOkButton = new JButton(\"   Ok   \");\n            hbox.add(goToOkButton);\n            goToOkButton.setEnabled(false);\n            goToOkButton.addActionListener(e -> {\n                okCallback.accept(Integer.valueOf(goToEnterLineNumberTextField.getText()));\n                goToDialog.setVisible(false);\n            });\n            hbox.add(Box.createHorizontalStrut(5));\n            JButton goToCancelButton = new JButton(\"Cancel\");\n            hbox.add(goToCancelButton);\n            Action goToCancelActionListener = new AbstractAction() {\n                public void actionPerformed(ActionEvent actionEvent) { goToDialog.setVisible(false); }\n            };\n            goToCancelButton.addActionListener(goToCancelActionListener);\n            vbox.add(hbox);\n\n            vbox.add(Box.createVerticalStrut(13));\n\n            // Last setup\n            JRootPane rootPane = goToDialog.getRootPane();\n            rootPane.setDefaultButton(goToOkButton);\n            rootPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), \"OpenTypeView.cancel\");\n            rootPane.getActionMap().put(\"OpenTypeView.cancel\", goToCancelActionListener);\n\n            // Add main listener\n            goToEnterLineNumberTextField.getDocument().addDocumentListener(new DocumentListener() {\n                protected Color backgroundColor = UIManager.getColor(\"TextField.background\");\n                protected Color errorBackgroundColor = Color.decode(configuration.getPreferences().get(\"JdGuiPreferences.errorBackgroundColor\"));\n\n                @Override public void insertUpdate(DocumentEvent e) { onTextChange(); }\n                @Override public void removeUpdate(DocumentEvent e) { onTextChange(); }\n                @Override public void changedUpdate(DocumentEvent e) { onTextChange(); }\n\n                protected void onTextChange() {\n                    String text = goToEnterLineNumberTextField.getText();\n\n                    if (text.length() == 0) {\n                        goToOkButton.setEnabled(false);\n                        clearErrorMessage();\n                    } else {\n                        try {\n                            int lineNumber = Integer.valueOf(text);\n\n                            if (lineNumber > navigator.getMaximumLineNumber()) {\n                                goToOkButton.setEnabled(false);\n                                showErrorMessage(\"Line number out of range\");\n                            } else if (navigator.checkLineNumber(lineNumber)) {\n                                goToOkButton.setEnabled(true);\n                                clearErrorMessage();\n                            } else {\n                                goToOkButton.setEnabled(false);\n                                showErrorMessage(\"Line number not found\");\n                            }\n                        } catch (NumberFormatException e) {\n                            goToOkButton.setEnabled(false);\n                            showErrorMessage(\"Not a number\");\n                        }\n                    }\n                }\n\n                protected void showErrorMessage(String message) {\n                    goToEnterLineNumberErrorLabel.setText(message);\n                    goToEnterLineNumberTextField.setBackground(errorBackgroundColor);\n                }\n\n                protected void clearErrorMessage() {\n                    goToEnterLineNumberErrorLabel.setText(\" \");\n                    goToEnterLineNumberTextField.setBackground(backgroundColor);\n                }\n            });\n\n            // Prepare to display\n            goToDialog.pack();\n            goToDialog.setLocationRelativeTo(mainFrame);\n        });\n    }\n\n    public void show(LineNumberNavigable navigator, IntConsumer okCallback) {\n        this.navigator = navigator;\n        this.okCallback = okCallback;\n\n        SwingUtil.invokeLater(() -> {\n            // Init\n            goToEnterLineNumberLabel.setText(\"Enter line number (1..\" + navigator.getMaximumLineNumber() + \"):\");\n            goToEnterLineNumberTextField.setText(\"\");\n            // Show\n            goToDialog.setVisible(true);\n            goToEnterLineNumberTextField.requestFocus();\n        });\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/view/MainView.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view;\n\nimport org.jd.gui.Constants;\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.*;\nimport org.jd.gui.model.configuration.Configuration;\nimport org.jd.gui.model.history.History;\nimport org.jd.gui.service.platform.PlatformService;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.view.component.IconButton;\nimport org.jd.gui.view.component.panel.MainTabbedPanel;\n\nimport javax.swing.*;\nimport javax.swing.border.Border;\nimport javax.swing.event.ChangeEvent;\nimport javax.swing.event.ChangeListener;\nimport javax.swing.text.BadLocationException;\nimport javax.swing.text.Document;\nimport java.awt.*;\nimport java.awt.event.ActionListener;\nimport java.awt.event.InputEvent;\nimport java.awt.event.KeyAdapter;\nimport java.awt.event.KeyEvent;\nimport java.io.File;\nimport java.net.URI;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\nimport static org.jd.gui.util.swing.SwingUtil.*;\n\n@SuppressWarnings(\"unchecked\")\npublic class MainView<T extends JComponent & UriGettable> implements UriOpenable, PreferencesChangeListener {\n    protected History history;\n    protected Consumer<File> openFilesCallback;\n    protected JFrame mainFrame;\n    protected JMenu recentFiles = new JMenu(\"Recent Files\");\n    protected Action closeAction;\n    protected Action openTypeAction;\n    protected Action backwardAction;\n    protected Action forwardAction;\n    protected MainTabbedPanel mainTabbedPanel;\n    protected Box findPanel;\n    protected JComboBox findComboBox;\n    protected JCheckBox findCaseSensitive;\n    protected Color findBackgroundColor;\n    protected Color findErrorBackgroundColor;\n\n    public MainView(\n            Configuration configuration, API api, History history,\n            ActionListener openActionListener,\n            ActionListener closeActionListener,\n            ActionListener saveActionListener,\n            ActionListener saveAllSourcesActionListener,\n            ActionListener exitActionListener,\n            ActionListener copyActionListener,\n            ActionListener pasteActionListener,\n            ActionListener selectAllActionListener,\n            ActionListener findActionListener,\n            ActionListener findPreviousActionListener,\n            ActionListener findNextActionListener,\n            ActionListener findCaseSensitiveActionListener,\n            Runnable findCriteriaChangedCallback,\n            ActionListener openTypeActionListener,\n            ActionListener openTypeHierarchyActionListener,\n            ActionListener goToActionListener,\n            ActionListener backwardActionListener,\n            ActionListener forwardActionListener,\n            ActionListener searchActionListener,\n            ActionListener jdWebSiteActionListener,\n            ActionListener jdGuiIssuesActionListener,\n            ActionListener jdCoreIssuesActionListener,\n            ActionListener preferencesActionListener,\n            ActionListener aboutActionListener,\n            Runnable panelClosedCallback,\n            Consumer<T> currentPageChangedCallback,\n            Consumer<File> openFilesCallback) {\n        this.history = history;\n        this.openFilesCallback = openFilesCallback;\n        // Build GUI\n        invokeLater(() -> {\n            mainFrame = new JFrame(\"Java Decompiler\");\n            mainFrame.setIconImages(Arrays.asList(getImage(\"/org/jd/gui/images/jd_icon_32.png\"), getImage(\"/org/jd/gui/images/jd_icon_64.png\"), getImage(\"/org/jd/gui/images/jd_icon_128.png\")));\n            mainFrame.setMinimumSize(new Dimension(Constants.MINIMAL_WIDTH, Constants.MINIMAL_HEIGHT));\n            mainFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);\n\n            // Find panel //\n            Action findNextAction = newAction(\"Next\", newImageIcon(\"/org/jd/gui/images/next_nav.png\"), true, findNextActionListener);\n            findPanel = Box.createHorizontalBox();\n            findPanel.setVisible(false);\n            findPanel.add(new JLabel(\"Find: \"));\n            findComboBox = new JComboBox();\n            findComboBox.setEditable(true);\n            JComponent editorComponent = (JComponent)findComboBox.getEditor().getEditorComponent();\n            editorComponent.addKeyListener(new KeyAdapter() {\n                protected String lastStr = \"\";\n\n                @Override\n                public void keyReleased(KeyEvent e) {\n                    switch (e.getKeyCode()) {\n                        case KeyEvent.VK_ESCAPE:\n                            findPanel.setVisible(false);\n                            break;\n                        case KeyEvent.VK_ENTER:\n                            String str = getFindText();\n                            if (str.length() > 1) {\n                                int index = ((DefaultComboBoxModel)findComboBox.getModel()).getIndexOf(str);\n                                if(index != -1 ) {\n                                    findComboBox.removeItemAt(index);\n                                }\n                                findComboBox.insertItemAt(str, 0);\n                                findComboBox.setSelectedIndex(0);\n                                findNextAction.actionPerformed(null);\n                            }\n                            break;\n                        default:\n                            str = getFindText();\n                            if (! lastStr.equals(str)) {\n                                findCriteriaChangedCallback.run();\n                                lastStr = str;\n                            }\n                    }\n                }\n            });\n            editorComponent.setOpaque(true);\n            findComboBox.setBackground(this.findBackgroundColor = editorComponent.getBackground());\n            this.findErrorBackgroundColor = Color.decode(configuration.getPreferences().get(\"JdGuiPreferences.errorBackgroundColor\"));\n\n            findPanel.add(findComboBox);\n            findPanel.add(Box.createHorizontalStrut(5));\n            JToolBar toolBar = new JToolBar();\n            toolBar.setFloatable(false);\n            toolBar.setRollover(true);\n\n            IconButton findNextButton = new IconButton(\"Next\", newAction(newImageIcon(\"/org/jd/gui/images/next_nav.png\"), true, findNextActionListener));\n            toolBar.add(findNextButton);\n\n            toolBar.add(Box.createHorizontalStrut(5));\n\n            IconButton findPreviousButton = new IconButton(\"Previous\", newAction(newImageIcon(\"/org/jd/gui/images/prev_nav.png\"), true, findPreviousActionListener));\n            toolBar.add(findPreviousButton);\n\n            findPanel.add(toolBar);\n            findCaseSensitive = new JCheckBox();\n            findCaseSensitive.setAction(newAction(\"Case sensitive\", true, findCaseSensitiveActionListener));\n            findPanel.add(findCaseSensitive);\n            findPanel.add(Box.createHorizontalGlue());\n\n            IconButton findCloseButton = new IconButton(newAction(null, null, true, e -> findPanel.setVisible(false)));\n            findCloseButton.setContentAreaFilled(false);\n            findCloseButton.setIcon(newImageIcon(\"/org/jd/gui/images/close.gif\"));\n            findCloseButton.setRolloverIcon(newImageIcon(\"/org/jd/gui/images/close_active.gif\"));\n            findPanel.add(findCloseButton);\n\n            if (PlatformService.getInstance().isMac()) {\n                findPanel.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));\n                Border border = BorderFactory.createEmptyBorder();\n                findNextButton.setBorder(border);\n                findPreviousButton.setBorder(border);\n                findCloseButton.setBorder(border);\n            } else {\n                findPanel.setBorder(BorderFactory.createEmptyBorder(2, 10, 2, 2));\n            }\n\n            // Actions //\n            boolean browser = Desktop.isDesktopSupported() ? Desktop.getDesktop().isSupported(Desktop.Action.BROWSE) : false;\n            Action openAction = newAction(\"Open File...\", newImageIcon(\"/org/jd/gui/images/open.png\"), true, \"Open a file\", openActionListener);\n            closeAction = newAction(\"Close\", false, closeActionListener);\n            Action saveAction = newAction(\"Save\", newImageIcon(\"/org/jd/gui/images/save.png\"), false, saveActionListener);\n            Action saveAllSourcesAction = newAction(\"Save All Sources\", newImageIcon(\"/org/jd/gui/images/save_all.png\"), false, saveAllSourcesActionListener);\n            Action exitAction = newAction(\"Exit\", true, \"Quit this program\", exitActionListener);\n            Action copyAction = newAction(\"Copy\", newImageIcon(\"/org/jd/gui/images/copy.png\"), false, copyActionListener);\n            Action pasteAction = newAction(\"Paste Log\", newImageIcon(\"/org/jd/gui/images/paste.png\"), true, pasteActionListener);\n            Action selectAllAction = newAction(\"Select all\", false, selectAllActionListener);\n            Action findAction = newAction(\"Find...\", false, findActionListener);\n            openTypeAction = newAction(\"Open Type...\", newImageIcon(\"/org/jd/gui/images/open_type.png\"), false, openTypeActionListener);\n            Action openTypeHierarchyAction = newAction(\"Open Type Hierarchy...\", false, openTypeHierarchyActionListener);\n            Action goToAction = newAction(\"Go to Line...\", false, goToActionListener);\n            backwardAction = newAction(\"Back\", newImageIcon(\"/org/jd/gui/images/backward_nav.png\"), false, backwardActionListener);\n            forwardAction = newAction(\"Forward\", newImageIcon(\"/org/jd/gui/images/forward_nav.png\"), false, forwardActionListener);\n            Action searchAction = newAction(\"Search...\", newImageIcon(\"/org/jd/gui/images/search_src.png\"), false, searchActionListener);\n            Action jdWebSiteAction = newAction(\"JD Web site\", browser, \"Open JD Web site\", jdWebSiteActionListener);\n            Action jdGuiIssuesActionAction = newAction(\"JD-GUI issues\", browser, \"Open JD-GUI issues page\", jdGuiIssuesActionListener);\n            Action jdCoreIssuesActionAction = newAction(\"JD-Core issues\", browser, \"Open JD-Core issues page\", jdCoreIssuesActionListener);\n            Action preferencesAction = newAction(\"Preferences...\", newImageIcon(\"/org/jd/gui/images/preferences.png\"), true, \"Open the preferences panel\", preferencesActionListener);\n            Action aboutAction = newAction(\"About...\", true, \"About JD-GUI\", aboutActionListener);\n\n            // Menu //\n            int menuShortcutKeyMask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();\n            JMenuBar menuBar = new JMenuBar();\n            JMenu menu = new JMenu(\"File\");\n            menuBar.add(menu);\n            menu.add(openAction).setAccelerator(KeyStroke.getKeyStroke('O', menuShortcutKeyMask));\n            menu.addSeparator();\n            menu.add(closeAction).setAccelerator(KeyStroke.getKeyStroke('W', menuShortcutKeyMask));\n            menu.addSeparator();\n            menu.add(saveAction).setAccelerator(KeyStroke.getKeyStroke('S', menuShortcutKeyMask));\n            menu.add(saveAllSourcesAction).setAccelerator(KeyStroke.getKeyStroke('S', menuShortcutKeyMask|InputEvent.ALT_MASK));\n            menu.addSeparator();\n            menu.add(recentFiles);\n            if (!PlatformService.getInstance().isMac()) {\n                menu.addSeparator();\n                menu.add(exitAction).setAccelerator(KeyStroke.getKeyStroke('X', InputEvent.ALT_MASK));\n            }\n            menu = new JMenu(\"Edit\");\n            menuBar.add(menu);\n            menu.add(copyAction).setAccelerator(KeyStroke.getKeyStroke('C', menuShortcutKeyMask));\n            menu.add(pasteAction).setAccelerator(KeyStroke.getKeyStroke('V', menuShortcutKeyMask));\n            menu.addSeparator();\n            menu.add(selectAllAction).setAccelerator(KeyStroke.getKeyStroke('A', menuShortcutKeyMask));\n            menu.addSeparator();\n            menu.add(findAction).setAccelerator(KeyStroke.getKeyStroke('F', menuShortcutKeyMask));\n            menu = new JMenu(\"Navigation\");\n            menuBar.add(menu);\n            menu.add(openTypeAction).setAccelerator(KeyStroke.getKeyStroke('T', menuShortcutKeyMask));\n            menu.add(openTypeHierarchyAction).setAccelerator(KeyStroke.getKeyStroke('H', menuShortcutKeyMask));\n            menu.addSeparator();\n            menu.add(goToAction).setAccelerator(KeyStroke.getKeyStroke('L', menuShortcutKeyMask));\n            menu.addSeparator();\n            menu.add(backwardAction).setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, InputEvent.ALT_MASK));\n            menu.add(forwardAction).setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, InputEvent.ALT_MASK));\n            menu = new JMenu(\"Search\");\n            menuBar.add(menu);\n            menu.add(searchAction).setAccelerator(KeyStroke.getKeyStroke('S', menuShortcutKeyMask|InputEvent.SHIFT_MASK));\n            menu = new JMenu(\"Help\");\n            menuBar.add(menu);\n            if (browser) {\n                menu.add(jdWebSiteAction);\n                menu.add(jdGuiIssuesActionAction);\n                menu.add(jdCoreIssuesActionAction);\n                menu.addSeparator();\n            }\n            menu.add(preferencesAction).setAccelerator(KeyStroke.getKeyStroke('P', menuShortcutKeyMask|InputEvent.SHIFT_MASK));\n            if (!PlatformService.getInstance().isMac()) {\n                menu.addSeparator();\n                menu.add(aboutAction).setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0));\n            }\n            mainFrame.setJMenuBar(menuBar);\n\n            // Icon bar //\n            JPanel panel = new JPanel();\n            panel.setLayout(new BorderLayout());\n            toolBar = new JToolBar();\n            toolBar.setFloatable(false);\n            toolBar.setRollover(true);\n            toolBar.add(new IconButton(openAction));\n            toolBar.addSeparator();\n            toolBar.add(new IconButton(openTypeAction));\n            toolBar.add(new IconButton(searchAction));\n            toolBar.addSeparator();\n            toolBar.add(new IconButton(backwardAction));\n            toolBar.add(new IconButton(forwardAction));\n            panel.add(toolBar, BorderLayout.PAGE_START);\n\n            mainTabbedPanel = new MainTabbedPanel(api);\n            mainTabbedPanel.getPageChangedListeners().add(new PageChangeListener() {\n                protected JComponent currentPage = null;\n\n                @Override public <U extends JComponent & UriGettable> void pageChanged(U page) {\n                    if (currentPage != page) {\n                        // Update current page\n                        currentPage = page;\n                        currentPageChangedCallback.accept((T)page);\n\n                        invokeLater(() -> {\n                            if (page == null) {\n                                // Update title\n                                mainFrame.setTitle(\"Java Decompiler\");\n                                // Update menu\n                                saveAction.setEnabled(false);\n                                copyAction.setEnabled(false);\n                                selectAllAction.setEnabled(false);\n                                openTypeHierarchyAction.setEnabled(false);\n                                goToAction.setEnabled(false);\n                                // Update find panel\n                                findPanel.setVisible(false);\n                            } else {\n                                // Update title\n                                String path = page.getUri().getPath();\n                                int index = path.lastIndexOf('/');\n                                String name = (index == -1) ? path : path.substring(index + 1);\n                                mainFrame.setTitle((name != null) ? name + \" - Java Decompiler\" : \"Java Decompiler\");\n                                // Update history\n                                history.add(page.getUri());\n                                // Update history actions\n                                updateHistoryActions();\n                                // Update menu\n                                saveAction.setEnabled(page instanceof ContentSavable);\n                                copyAction.setEnabled(page instanceof ContentCopyable);\n                                selectAllAction.setEnabled(page instanceof ContentSelectable);\n                                findAction.setEnabled(page instanceof ContentSearchable);\n                                openTypeHierarchyAction.setEnabled(page instanceof FocusedTypeGettable);\n                                goToAction.setEnabled(page instanceof LineNumberNavigable);\n                                // Update find panel\n                                if (findPanel.isVisible()) {\n                                    findPanel.setVisible(page instanceof ContentSearchable);\n                                }\n                            }\n                        });\n                    }\n                }\n            });\n            mainTabbedPanel.getTabbedPane().addChangeListener(new ChangeListener() {\n                protected int lastTabCount = 0;\n\n                @Override\n                public void stateChanged(ChangeEvent e) {\n                    int tabCount = mainTabbedPanel.getTabbedPane().getTabCount();\n                    boolean enabled = (tabCount > 0);\n\n                    closeAction.setEnabled(enabled);\n                    openTypeAction.setEnabled(enabled);\n                    searchAction.setEnabled(enabled);\n                    saveAllSourcesAction.setEnabled((mainTabbedPanel.getTabbedPane().getSelectedComponent() instanceof SourcesSavable));\n\n                    if (tabCount < lastTabCount) {\n                        panelClosedCallback.run();\n                    }\n\n                    lastTabCount = tabCount;\n                }\n            });\n            mainTabbedPanel.preferencesChanged(configuration.getPreferences());\n            panel.add(mainTabbedPanel, BorderLayout.CENTER);\n\n            panel.add(findPanel, BorderLayout.PAGE_END);\n            mainFrame.add(panel);\n        });\n    }\n\n    public void show(Point location, Dimension size, boolean maximize) {\n        invokeLater(() -> {\n            // Set position, resize and show\n            mainFrame.setLocation(location);\n            mainFrame.setSize(size);\n            mainFrame.setExtendedState(maximize ? JFrame.MAXIMIZED_BOTH : 0);\n            mainFrame.setVisible(true);\n        });\n    }\n\n    public JFrame getMainFrame() {\n        return mainFrame;\n    }\n\n    public void showFindPanel() {\n        invokeLater(() -> {\n            findPanel.setVisible(true);\n            findComboBox.requestFocus();\n        });\n    }\n\n    public void setFindBackgroundColor(boolean wasFound) {\n        invokeLater(() -> {\n            findComboBox.getEditor().getEditorComponent().setBackground(wasFound ? findBackgroundColor : findErrorBackgroundColor);\n        });\n    }\n\n    public <T extends JComponent & UriGettable> void addMainPanel(String title, Icon icon, String tip, T component) {\n        invokeLater(() -> {\n            mainTabbedPanel.addPage(title, icon, tip, component);\n        });\n    }\n\n    public <T extends JComponent & UriGettable> List<T> getMainPanels() {\n        return mainTabbedPanel.getPages();\n    }\n\n    public <T extends JComponent & UriGettable> T getSelectedMainPanel() {\n        return (T)mainTabbedPanel.getTabbedPane().getSelectedComponent();\n    }\n\n    public void closeCurrentTab() {\n        invokeLater(() -> {\n            Component component = mainTabbedPanel.getTabbedPane().getSelectedComponent();\n            if (component instanceof PageClosable) {\n                if (!((PageClosable)component).closePage()) {\n                    mainTabbedPanel.removeComponent(component);\n                }\n            } else {\n                mainTabbedPanel.removeComponent(component);\n            }\n        });\n    }\n\n    public void updateRecentFilesMenu(List<File> files) {\n        invokeLater(() -> {\n            recentFiles.removeAll();\n\n            for (File file : files) {\n                JMenuItem menuItem = new JMenuItem(reduceRecentFilePath(file.getAbsolutePath()));\n                menuItem.addActionListener(e -> openFilesCallback.accept(file));\n                recentFiles.add(menuItem);\n            }\n        });\n    }\n\n    public String getFindText() {\n        Document doc = ((JTextField)findComboBox.getEditor().getEditorComponent()).getDocument();\n\n        try {\n            return doc.getText(0, doc.getLength());\n        } catch (BadLocationException e) {\n            assert ExceptionUtil.printStackTrace(e);\n            return \"\";\n        }\n    }\n\n    public boolean getFindCaseSensitive() { return findCaseSensitive.isSelected(); }\n\n    public void updateHistoryActions() {\n        invokeLater(() -> {\n            backwardAction.setEnabled(history.canBackward());\n            forwardAction.setEnabled(history.canForward());\n        });\n    }\n\n    // --- Utils --- //\n    static String reduceRecentFilePath(String path) {\n        int lastSeparatorPosition = path.lastIndexOf(File.separatorChar);\n\n        if ((lastSeparatorPosition == -1) || (lastSeparatorPosition < Constants.RECENT_FILE_MAX_LENGTH)) {\n            return path;\n        }\n\n        int length = Constants.RECENT_FILE_MAX_LENGTH/2 - 2;\n        String left = path.substring(0, length);\n        String right = path.substring(path.length() - length);\n\n        return left + \"...\" + right;\n    }\n\n    // --- URIOpener --- //\n    @Override\n    public boolean openUri(URI uri) {\n        boolean success = mainTabbedPanel.openUri(uri);\n\n        if (success) {\n            closeAction.setEnabled(true);\n            openTypeAction.setEnabled(true);\n        }\n\n        return success;\n    }\n\n    // --- PreferencesChangeListener --- //\n    @Override\n    public void preferencesChanged(Map<String, String> preferences) {\n        mainTabbedPanel.preferencesChanged(preferences);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/view/OpenTypeHierarchyView.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.api.model.TreeNodeData;\nimport org.jd.gui.api.model.Type;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.util.function.TriConsumer;\nimport org.jd.gui.util.swing.SwingUtil;\nimport org.jd.gui.view.component.Tree;\nimport org.jd.gui.view.renderer.TreeNodeRenderer;\n\nimport javax.swing.*;\nimport javax.swing.event.TreeExpansionEvent;\nimport javax.swing.event.TreeExpansionListener;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport javax.swing.tree.DefaultTreeModel;\nimport javax.swing.tree.TreePath;\nimport java.awt.*;\nimport java.awt.event.*;\nimport java.util.*;\nimport java.util.List;\nimport java.util.concurrent.Future;\n\npublic class OpenTypeHierarchyView {\n    protected static final ImageIcon ROOT_CLASS_ICON = new ImageIcon(OpenTypeHierarchyView.class.getClassLoader().getResource(\"org/jd/gui/images/generate_class.png\"));\n    protected static final ImageIcon ROOT_INTERFACE_ICON = new ImageIcon(OpenTypeHierarchyView.class.getClassLoader().getResource(\"org/jd/gui/images/generate_int.png\"));\n\n    protected static final TreeNodeComparator TREE_NODE_COMPARATOR = new TreeNodeComparator();\n\n    protected API api;\n    protected Collection<Future<Indexes>> collectionOfFutureIndexes;\n\n    protected JDialog openTypeHierarchyDialog;\n    protected Tree openTypeHierarchyTree;\n\n    protected TriConsumer<Point, Collection<Container.Entry>, String> selectedTypeCallback;\n\n    public OpenTypeHierarchyView(API api, JFrame mainFrame, TriConsumer<Point, Collection<Container.Entry>, String> selectedTypeCallback) {\n        this.api = api;\n        this.selectedTypeCallback = selectedTypeCallback;\n        // Build GUI\n        SwingUtil.invokeLater(() -> {\n            openTypeHierarchyDialog = new JDialog(mainFrame, \"Hierarchy Type\", false);\n\n            JPanel panel = new JPanel();\n            panel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));\n            panel.setLayout(new BorderLayout());\n            openTypeHierarchyDialog.add(panel);\n\n            openTypeHierarchyTree = new Tree();\n            openTypeHierarchyTree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode()));\n            openTypeHierarchyTree.setCellRenderer(new TreeNodeRenderer());\n            openTypeHierarchyTree.addMouseListener(new MouseAdapter() {\n                @Override public void mouseClicked(MouseEvent e) {\n                    if (e.getClickCount() == 2) {\n                        onTypeSelected();\n                    }\n                }\n            });\n            openTypeHierarchyTree.addTreeExpansionListener(new TreeExpansionListener() {\n                @Override public void treeExpanded(TreeExpansionEvent e) {\n                    TreeNode node = (TreeNode)e.getPath().getLastPathComponent();\n                    // Expand node and find the first leaf\n                    while (node.getChildCount() > 0) {\n                        if (((DefaultMutableTreeNode)node.getChildAt(0)).getUserObject() == null) {\n                            // Remove dummy node and create children\n                            populateTreeNode(node, null);\n                        }\n                        if (node.getChildCount() != 1) {\n                            break;\n                        }\n                        node = ((TreeNode)node.getChildAt(0));\n                    }\n                    DefaultTreeModel model = (DefaultTreeModel)openTypeHierarchyTree.getModel();\n                    model.reload((TreeNode)e.getPath().getLastPathComponent());\n                    openTypeHierarchyTree.setSelectionPath(new TreePath(node.getPath()));\n                }\n                @Override public void treeCollapsed(TreeExpansionEvent e) {}\n            });\n            openTypeHierarchyTree.addKeyListener(new KeyAdapter() {\n                @Override public void keyPressed(KeyEvent e) {\n                    if (e.getKeyCode() == KeyEvent.VK_F4) {\n                        TreeNode node = (TreeNode)openTypeHierarchyTree.getLastSelectedPathComponent();\n                        if (node != null) {\n                            updateTree(node.entry, node.typeName);\n                        }\n                    }\n                }\n            });\n\n            JScrollPane openTypeHierarchyScrollPane = new JScrollPane(openTypeHierarchyTree);\n            openTypeHierarchyScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);\n            openTypeHierarchyScrollPane.setPreferredSize(new Dimension(400, 150));\n            panel.add(openTypeHierarchyScrollPane, BorderLayout.CENTER);\n\n            // Buttons \"Open\" and \"Cancel\"\n            Box vbox = Box.createVerticalBox();\n            panel.add(vbox, BorderLayout.SOUTH);\n            vbox.add(Box.createVerticalStrut(25));\n            Box hbox = Box.createHorizontalBox();\n            vbox.add(hbox);\n            hbox.add(Box.createHorizontalGlue());\n            JButton openTypeHierarchyOpenButton = new JButton(\"Open\");\n            hbox.add(openTypeHierarchyOpenButton);\n            openTypeHierarchyOpenButton.setEnabled(false);\n            openTypeHierarchyOpenButton.addActionListener(e -> onTypeSelected());\n            hbox.add(Box.createHorizontalStrut(5));\n            JButton openTypeHierarchyCancelButton = new JButton(\"Cancel\");\n            hbox.add(openTypeHierarchyCancelButton);\n            Action openTypeHierarchyCancelActionListener = new AbstractAction() {\n                @Override public void actionPerformed(ActionEvent actionEvent) { openTypeHierarchyDialog.setVisible(false); }\n            };\n            openTypeHierarchyCancelButton.addActionListener(openTypeHierarchyCancelActionListener);\n\n            openTypeHierarchyTree.addTreeSelectionListener(e -> {\n                Object o = openTypeHierarchyTree.getLastSelectedPathComponent();\n                if (o != null) {\n                    o = ((TreeNode)o).entry;\n                }\n                openTypeHierarchyOpenButton.setEnabled(o != null);\n            });\n\n            // Last setup\n            JRootPane rootPane = openTypeHierarchyDialog.getRootPane();\n            rootPane.setDefaultButton(openTypeHierarchyOpenButton);\n            rootPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), \"OpenTypeHierarchyView.cancel\");\n            rootPane.getActionMap().put(\"OpenTypeHierarchyView.cancel\", openTypeHierarchyCancelActionListener);\n\n            openTypeHierarchyDialog.setMinimumSize(openTypeHierarchyDialog.getSize());\n\n            // Prepare to display\n            openTypeHierarchyDialog.pack();\n            openTypeHierarchyDialog.setLocationRelativeTo(mainFrame);\n        });\n    }\n\n    public void show(Collection<Future<Indexes>> collectionOfFutureIndexes, Container.Entry entry, String typeName) {\n        this.collectionOfFutureIndexes = collectionOfFutureIndexes;\n        SwingUtil.invokeLater(() -> {\n            updateTree(entry, typeName);\n            openTypeHierarchyDialog.setVisible(true);\n            openTypeHierarchyTree.requestFocus();\n        });\n    }\n\n    public boolean isVisible() { return openTypeHierarchyDialog.isVisible(); }\n\n    public void showWaitCursor() {\n        SwingUtil.invokeLater(() -> openTypeHierarchyDialog.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)));\n    }\n\n    public void hideWaitCursor() {\n        SwingUtil.invokeLater(() -> openTypeHierarchyDialog.setCursor(Cursor.getDefaultCursor()));\n    }\n\n    public void updateTree(Collection<Future<Indexes>> collectionOfFutureIndexes) {\n        this.collectionOfFutureIndexes = collectionOfFutureIndexes;\n        TreeNode selectedTreeNode = (TreeNode)openTypeHierarchyTree.getLastSelectedPathComponent();\n\n        if (selectedTreeNode != null) {\n            updateTree(selectedTreeNode.entry, selectedTreeNode.typeName);\n        }\n    }\n\n    protected void updateTree(Container.Entry entry, String typeName) {\n        SwingUtil.invokeLater(() -> {\n            // Clear tree\n            DefaultTreeModel model = (DefaultTreeModel)openTypeHierarchyTree.getModel();\n            DefaultMutableTreeNode root = (DefaultMutableTreeNode)model.getRoot();\n            root.removeAllChildren();\n\n            TreeNode selectedTreeNode = createTreeNode(entry, typeName);\n            TreeNode parentTreeNode = createParentTreeNode(selectedTreeNode);\n\n            root.add(parentTreeNode);\n            model.reload();\n\n            if (selectedTreeNode != null) {\n                TreePath path = new TreePath(selectedTreeNode.getPath());\n                // Expand\n                openTypeHierarchyTree.expandPath(path);\n                // Scroll to show tree node\n                openTypeHierarchyTree.makeVisible(path);\n                Rectangle bounds = openTypeHierarchyTree.getPathBounds(path);\n\n                if(bounds != null) {\n                    bounds.x = 0;\n\n                    Rectangle lastRowBounds = openTypeHierarchyTree.getRowBounds(openTypeHierarchyTree.getRowCount()-1);\n\n                    if (lastRowBounds != null) {\n                        bounds.y = Math.max(bounds.y-30, 0);\n                        bounds.height = Math.min(bounds.height+bounds.y+60, lastRowBounds.height+lastRowBounds.y) - bounds.y;\n                    }\n\n                    openTypeHierarchyTree.scrollRectToVisible(bounds);\n                    openTypeHierarchyTree.scrollPathToVisible(path);\n                    openTypeHierarchyTree.fireVisibleDataPropertyChange();\n                }\n                // Select tree node\n                openTypeHierarchyTree.setSelectionPath(path);\n            }\n        });\n    }\n\n    protected TreeNode createTreeNode(Container.Entry entry, String typeName) {\n        Type type = api.getTypeFactory(entry).make(api, entry, typeName);\n\n        typeName = type.getName();\n\n        List<Container.Entry> entries = getEntries(typeName);\n        TreeNode treeNode = new TreeNode(entry, typeName, entries, new TreeNodeBean(type));\n        List<String> childTypeNames = getSubTypeNames(typeName);\n\n        if (childTypeNames != null) {\n            // Add dummy node\n            treeNode.add(new DefaultMutableTreeNode());\n        }\n\n        return treeNode;\n    }\n\n    /**\n     * Create parent and sibling tree nodes\n     */\n    protected TreeNode createParentTreeNode(TreeNode treeNode) {\n        Type type = api.getTypeFactory(treeNode.entry).make(api, treeNode.entry, treeNode.typeName);\n        String superTypeName = type.getSuperName();\n\n        if (superTypeName != null) {\n            List<Container.Entry> superEntries = getEntries(superTypeName);\n\n            // Search entry in the sane container of 'entry'\n            Container.Entry superEntry = null;\n\n            if ((superEntries != null) && !superEntries.isEmpty()) {\n                for (Container.Entry se : superEntries) {\n                    if (se.getContainer() == treeNode.entry.getContainer()) {\n                        superEntry = se;\n                        break;\n                    }\n                }\n\n                if (superEntry == null) {\n                    // Not found -> Choose 1st one\n                    superEntry = superEntries.get(0);\n                }\n            } else {\n                superEntry = null;\n            }\n\n            if (superEntry != null) {\n                // Create parent tree node\n                TreeNode superTreeNode = createTreeNode(superEntry, superTypeName);\n                // Populate parent tree node\n                populateTreeNode(superTreeNode, treeNode);\n                // Recursive call\n                return createParentTreeNode(superTreeNode);\n            } else {\n                // Entry not found --> Most probable hypothesis : Java type entry\n                int lastPackageSeparatorIndex = superTypeName.lastIndexOf('/');\n                String package_ = superTypeName.substring(0, lastPackageSeparatorIndex).replace('/', '.');\n                String name = superTypeName.substring(lastPackageSeparatorIndex + 1).replace('$', '.');\n                String label = (package_ != null) ? name + \" - \" + package_ : name;\n                Icon icon = ((type.getFlags() & Type.FLAG_INTERFACE) == 0) ? ROOT_CLASS_ICON : ROOT_INTERFACE_ICON;\n                TreeNode rootTreeNode = new TreeNode(null, superTypeName, null, new TreeNodeBean(label, icon));\n\n                if (package_.startsWith(\"java.\")) {\n                    // If root type is a JDK type, do not create a tree node for each child types\n                    rootTreeNode.add(treeNode);\n                } else {\n                    populateTreeNode(rootTreeNode, treeNode);\n                }\n\n                return rootTreeNode;\n            }\n        } else {\n            // super type undefined\n            return treeNode;\n        }\n    }\n\n    /**\n     * @param superTreeNode  node to populate\n     * @param activeTreeNode active child node\n     */\n    protected void populateTreeNode(TreeNode superTreeNode, TreeNode activeTreeNode) {\n        superTreeNode.removeAllChildren();\n\n        // Search preferred container: if 'superTreeNode' is a root with an unknown super entry, uses the container of active child node\n        Container.Entry notNullEntry = superTreeNode.entry;\n\n        if (notNullEntry == null) {\n            notNullEntry = activeTreeNode.entry;\n        }\n\n        Container preferredContainer = notNullEntry.getContainer();\n        String activeTypName = null;\n\n        if (activeTreeNode != null) {\n            activeTypName = activeTreeNode.typeName;\n        }\n\n        List<String> subTypeNames = getSubTypeNames(superTreeNode.typeName);\n        ArrayList<TreeNode> treeNodes = new ArrayList<>();\n\n        for (String subTypeName : subTypeNames) {\n            if (subTypeName.equals(activeTypName)) {\n                treeNodes.add(activeTreeNode);\n            } else {\n                // Search entry in the sane container of 'superTreeNode.entry'\n                List<Container.Entry> entries = getEntries(subTypeName);\n                Container.Entry entry = null;\n\n                for (Container.Entry e : entries) {\n                    if (e.getContainer() == preferredContainer) {\n                        entry = e;\n                    }\n                }\n\n                if (entry == null) {\n                    // Not found -> Choose 1st one\n                    entry = entries.get(0);\n                }\n                if (entry != null) {\n                    // Create type\n                    Type t = api.getTypeFactory(entry).make(api, entry, subTypeName);\n                    if (t != null) {\n                        // Create tree node\n                        treeNodes.add(createTreeNode(entry, t.getName()));\n                    }\n                }\n            }\n        }\n\n        treeNodes.sort(TREE_NODE_COMPARATOR);\n\n        for (TreeNode treeNode : treeNodes) {\n            superTreeNode.add(treeNode);\n        }\n    }\n\n    public void focus() {\n        SwingUtil.invokeLater(() -> openTypeHierarchyTree.requestFocus());\n    }\n\n    protected void onTypeSelected() {\n        TreeNode selectedTreeNode = (TreeNode)openTypeHierarchyTree.getLastSelectedPathComponent();\n\n        if (selectedTreeNode != null) {\n            TreePath path = new TreePath(selectedTreeNode.getPath());\n            Rectangle bounds = openTypeHierarchyTree.getPathBounds(path);\n            Point listLocation = openTypeHierarchyTree.getLocationOnScreen();\n            Point leftBottom = new Point(listLocation.x+bounds.x, listLocation.y+bounds.y+bounds.height);\n            selectedTypeCallback.accept(leftBottom, selectedTreeNode.entries, selectedTreeNode.typeName);\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected List<String> getSubTypeNames(String typeName) {\n        ArrayList<String> result = new ArrayList<>();\n\n        try {\n            for (Future<Indexes> futureIndexes : collectionOfFutureIndexes) {\n                if (futureIndexes.isDone()) {\n                    Map<String, Collection> subTypeNames = futureIndexes.get().getIndex(\"subTypeNames\");\n                    if (subTypeNames != null) {\n                        Collection<String> collection = subTypeNames.get(typeName);\n                        if (collection != null) {\n                            for (String tn : collection) {\n                                if (tn != null) {\n                                    result.add(tn);\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        } catch (Exception e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n\n        return result;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected List<Container.Entry> getEntries(String typeName) {\n        ArrayList<Container.Entry> result = new ArrayList<>();\n\n        try {\n            for (Future<Indexes> futureIndexes : collectionOfFutureIndexes) {\n                if (futureIndexes.isDone()) {\n                    Map<String, Collection> typeDeclarations = futureIndexes.get().getIndex(\"typeDeclarations\");\n                    if (typeDeclarations != null) {\n                        Collection<Container.Entry> collection = typeDeclarations.get(typeName);\n                        if (collection != null) {\n                            for (Container.Entry e : collection) {\n                                if (e != null) {\n                                    result.add(e);\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        } catch (Exception e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n\n        return result;\n    }\n\n    protected static class TreeNode extends DefaultMutableTreeNode {\n        Container.Entry entry;\n        String typeName;\n        List<Container.Entry> entries;\n\n        TreeNode(Container.Entry entry, String typeName, List<Container.Entry> entries, Object userObject) {\n            super(userObject);\n            this.entry = entry;\n            this.typeName = typeName;\n            this.entries = entries;\n        }\n    }\n\n    // Graphic data for renderer\n    protected static class TreeNodeBean implements TreeNodeData {\n        String label;\n        String tip;\n        Icon icon;\n        Icon openIcon;\n\n        TreeNodeBean(Type type) {\n            this.label = (type.getDisplayPackageName() != null) ? type.getDisplayTypeName() + \" - \" + type.getDisplayPackageName() : type.getDisplayTypeName();\n            this.icon = type.getIcon();\n        }\n\n        TreeNodeBean(String label, Icon icon) {\n            this.label = label;\n            this.icon = icon;\n        }\n\n        @Override public String getLabel() { return label; }\n        @Override public String getTip() { return tip; }\n        @Override public Icon getIcon() { return icon; }\n        @Override public Icon getOpenIcon() { return openIcon; }\n    }\n\n    protected static class TreeNodeComparator implements Comparator<TreeNode> {\n        @Override\n        public int compare(TreeNode tn1, TreeNode tn2) {\n            return ((TreeNodeBean)tn1.getUserObject()).label.compareTo(((TreeNodeBean)tn2.getUserObject()).label);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/view/OpenTypeView.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Type;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.util.function.TriConsumer;\nimport org.jd.gui.util.swing.SwingUtil;\nimport org.jd.gui.view.bean.OpenTypeListCellBean;\nimport org.jd.gui.view.renderer.OpenTypeListCellRenderer;\n\nimport javax.swing.*;\nimport javax.swing.event.DocumentEvent;\nimport javax.swing.event.DocumentListener;\nimport javax.swing.text.BadLocationException;\nimport java.awt.*;\nimport java.awt.event.*;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Comparator;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\npublic class OpenTypeView {\n    protected static final int MAX_LINE_COUNT = 80;\n    protected static final TypeNameComparator TYPE_NAME_COMPARATOR = new TypeNameComparator();\n\n    protected API api;\n\n    protected JDialog openTypeDialog;\n    protected JTextField openTypeEnterTextField;\n    protected JLabel openTypeMatchLabel;\n    protected JList openTypeList;\n\n    @SuppressWarnings(\"unchecked\")\n    public OpenTypeView(API api, JFrame mainFrame, Consumer<String> changedPatternCallback, TriConsumer<Point, Collection<Container.Entry>, String> selectedTypeCallback) {\n        this.api = api;\n        // Build GUI\n        SwingUtil.invokeLater(() -> {\n            openTypeDialog = new JDialog(mainFrame, \"Open Type\", false);\n\n            JPanel panel = new JPanel();\n            panel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));\n            panel.setLayout(new BorderLayout());\n            openTypeDialog.add(panel);\n\n            // Box for \"Select a type to open\"\n            Box vbox = Box.createVerticalBox();\n            panel.add(vbox, BorderLayout.NORTH);\n\n            Box hbox = Box.createHorizontalBox();\n            hbox.add(new JLabel(\"Select a type to open (* = any string, ? = any character, TZ = TimeZone):\"));\n            hbox.add(Box.createHorizontalGlue());\n            vbox.add(hbox);\n\n            vbox.add(Box.createVerticalStrut(10));\n\n            // Text field\n            vbox.add(openTypeEnterTextField = new JTextField(30));\n            openTypeEnterTextField.addKeyListener(new KeyAdapter() {\n                @Override public void keyTyped(KeyEvent e) {\n                    switch (e.getKeyChar()) {\n                        case '=': case '(': case ')': case '{': case '}': case '[': case ']':\n                            e.consume();\n                            break;\n                        default:\n                            if (Character.isDigit(e.getKeyChar()) && (openTypeEnterTextField.getText().length() == 0)) {\n                                // First character can not be a digit\n                                e.consume();\n                            }\n                            break;\n                    }\n                }\n                @Override public void keyPressed(KeyEvent e) {\n                    if ((e.getKeyCode() == KeyEvent.VK_DOWN) && (openTypeList.getModel().getSize() > 0)) {\n                        openTypeList.setSelectedIndex(0);\n                        openTypeList.requestFocus();\n                        e.consume();\n                    }\n                }\n            });\n            openTypeEnterTextField.addFocusListener(new FocusListener() {\n                @Override public void focusGained(FocusEvent e) { openTypeList.clearSelection(); }\n                @Override public void focusLost(FocusEvent e) {}\n            });\n            openTypeEnterTextField.getDocument().addDocumentListener(new DocumentListener() {\n                @Override public void insertUpdate(DocumentEvent e) { call(e); }\n                @Override public void removeUpdate(DocumentEvent e) { call(e); }\n                @Override public void changedUpdate(DocumentEvent e) { call(e); }\n                protected void call(DocumentEvent e) {\n                    try {\n                        changedPatternCallback.accept(e.getDocument().getText(0, e.getDocument().getLength()));\n                    } catch (BadLocationException ex) {\n                        assert ExceptionUtil.printStackTrace(ex);\n                    }\n                }\n            });\n\n            vbox.add(Box.createVerticalStrut(10));\n\n            hbox = Box.createHorizontalBox();\n            hbox.add(openTypeMatchLabel = new JLabel(\"Matching types:\"));\n            hbox.add(Box.createHorizontalGlue());\n            vbox.add(hbox);\n\n            vbox.add(Box.createVerticalStrut(10));\n\n            // List of types\n            JScrollPane scrollPane = new JScrollPane(openTypeList = new JList());\n            openTypeList.addKeyListener(new KeyAdapter() {\n                @Override public void keyPressed(KeyEvent e) {\n                    if ((e.getKeyCode() == KeyEvent.VK_UP) && (openTypeList.getSelectedIndex()  == 0)) {\n                        openTypeEnterTextField.requestFocus();\n                        e.consume();\n                    }\n                }\n            });\n            openTypeList.setModel(new DefaultListModel<OpenTypeListCellBean>());\n            openTypeList.setCellRenderer(new OpenTypeListCellRenderer());\n            openTypeList.addMouseListener(new MouseAdapter() {\n                @Override public void mouseClicked(MouseEvent e) {\n                    if (e.getClickCount() == 2) {\n                        onTypeSelected(selectedTypeCallback);\n                    }\n                }\n            });\n            scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);\n            scrollPane.setPreferredSize(new Dimension(400, 150));\n            panel.add(scrollPane, BorderLayout.CENTER);\n\n            // Buttons \"Open\" and \"Cancel\"\n            vbox = Box.createVerticalBox();\n            panel.add(vbox, BorderLayout.SOUTH);\n            vbox.add(Box.createVerticalStrut(25));\n            vbox.add(hbox = Box.createHorizontalBox());\n            hbox.add(Box.createHorizontalGlue());\n            JButton openTypeOpenButton = new JButton(\"Open\");\n            hbox.add(openTypeOpenButton);\n            openTypeOpenButton.setEnabled(false);\n            openTypeOpenButton.addActionListener(e -> onTypeSelected(selectedTypeCallback));\n            hbox.add(Box.createHorizontalStrut(5));\n            JButton openTypeCancelButton = new JButton(\"Cancel\");\n            hbox.add(openTypeCancelButton);\n            Action openTypeCancelActionListener = new AbstractAction() {\n                @Override public void actionPerformed(ActionEvent actionEvent) { openTypeDialog.setVisible(false); }\n            };\n            openTypeCancelButton.addActionListener(openTypeCancelActionListener);\n\n            // Last setup\n            JRootPane rootPane = openTypeDialog.getRootPane();\n            rootPane.setDefaultButton(openTypeOpenButton);\n            rootPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), \"OpenTypeView.cancel\");\n            rootPane.getActionMap().put(\"OpenTypeView.cancel\", openTypeCancelActionListener);\n\n            openTypeList.addListSelectionListener(e -> openTypeOpenButton.setEnabled(openTypeList.getSelectedValue() != null));\n\n            openTypeDialog.setMinimumSize(openTypeDialog.getSize());\n\n            // Prepare to display\n            openTypeDialog.pack();\n            openTypeDialog.setLocationRelativeTo(mainFrame);\n        });\n    }\n\n    public void show() {\n        SwingUtil.invokeLater(() -> {\n            // Init\n            openTypeEnterTextField.selectAll();\n            // Show\n            openTypeDialog.setVisible(true);\n            openTypeEnterTextField.requestFocus();\n        });\n    }\n\n    public boolean isVisible() { return openTypeDialog.isVisible(); }\n\n    public String getPattern() { return openTypeEnterTextField.getText(); }\n\n    public void showWaitCursor() {\n        SwingUtil.invokeLater(() -> openTypeDialog.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)));\n    }\n\n    public void hideWaitCursor() {\n        SwingUtil.invokeLater(() -> openTypeDialog.setCursor(Cursor.getDefaultCursor()));\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public void updateList(Map<String, Collection<Container.Entry>> map) {\n        SwingUtil.invokeLater(() -> {\n            DefaultListModel model = (DefaultListModel)openTypeList.getModel();\n            ArrayList<String> typeNames = new ArrayList<>(map.keySet());\n            int index = 0;\n\n            typeNames.sort(TYPE_NAME_COMPARATOR);\n\n            model.removeAllElements();\n\n            for (String typeName : typeNames) {\n                if (index < MAX_LINE_COUNT) {\n                    Collection<Container.Entry> entries = map.get(typeName);\n                    Container.Entry firstEntry = entries.iterator().next();\n                    Type type = api.getTypeFactory(firstEntry).make(api, firstEntry, typeName);\n\n                    if (type != null) {\n                        model.addElement(new OpenTypeListCellBean(type.getDisplayTypeName(), type.getDisplayPackageName(), type.getIcon(), entries, typeName));\n                    } else {\n                        model.addElement(new OpenTypeListCellBean(typeName, entries, typeName));\n                    }\n                } else if (index == MAX_LINE_COUNT) {\n                    model.addElement(null);\n                }\n            }\n\n            int count = typeNames.size();\n\n            switch (count) {\n                case 0:\n                    openTypeMatchLabel.setText(\"Matching types:\");\n                    break;\n                case 1:\n                    openTypeMatchLabel.setText(\"1 matching type:\");\n                    break;\n                default:\n                    openTypeMatchLabel.setText(count + \" matching types:\");\n            }\n        });\n    }\n\n    public void focus() {\n        SwingUtil.invokeLater(() -> {\n            openTypeList.requestFocus();\n        });\n    }\n\n    protected void onTypeSelected(TriConsumer<Point, Collection<Container.Entry>, String> selectedTypeCallback) {\n        SwingUtil.invokeLater(() -> {\n            int index = openTypeList.getSelectedIndex();\n\n            if (index != -1) {\n                OpenTypeListCellBean selectedCellBean = (OpenTypeListCellBean)openTypeList.getModel().getElementAt(index);\n                Point listLocation = openTypeList.getLocationOnScreen();\n                Rectangle cellBound = openTypeList.getCellBounds(index, index);\n                Point leftBottom = new Point(listLocation.x + cellBound.x, listLocation.y + cellBound.y + cellBound.height);\n                selectedTypeCallback.accept(leftBottom, selectedCellBean.entries, selectedCellBean.typeName);\n            }\n        });\n    }\n\n    protected static class TypeNameComparator implements Comparator<String> {\n        @Override\n        public int compare(String tn1, String tn2) {\n            int lasPackageSeparatorIndex = tn1.lastIndexOf('/');\n            String shortName1 = tn1.substring(lasPackageSeparatorIndex+1);\n\n            lasPackageSeparatorIndex = tn2.lastIndexOf('/');\n            String shortName2 = tn2.substring(lasPackageSeparatorIndex+1);\n\n            return shortName1.compareTo(shortName2);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/view/PreferencesView.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view;\n\nimport org.jd.gui.model.configuration.Configuration;\nimport org.jd.gui.spi.PreferencesPanel;\nimport org.jd.gui.util.swing.SwingUtil;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.KeyEvent;\nimport java.util.*;\n\npublic class PreferencesView implements PreferencesPanel.PreferencesPanelChangeListener {\n    protected Map<String, String> preferences;\n    protected Collection<PreferencesPanel> panels;\n    protected HashMap<PreferencesPanel, Boolean> valids = new HashMap<>();\n\n    protected JDialog preferencesDialog;\n    protected JButton preferencesOkButton = new JButton();\n\n    protected Runnable okCallback;\n\n    public PreferencesView(Configuration configuration, JFrame mainFrame, Collection<PreferencesPanel> panels) {\n        this.preferences = configuration.getPreferences();\n        this.panels = panels;\n        // Build GUI\n        SwingUtil.invokeLater(() -> {\n            preferencesDialog = new JDialog(mainFrame, \"Preferences\", false);\n\n            JPanel panel = new JPanel();\n            panel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));\n            panel.setLayout(new BorderLayout());\n            preferencesDialog.add(panel);\n\n            // Box for preferences panels\n            Box preferencesPanels = Box.createVerticalBox();\n            preferencesPanels.setBackground(panel.getBackground());\n            preferencesPanels.setOpaque(true);\n            Color errorBackgroundColor = Color.decode(configuration.getPreferences().get(\"JdGuiPreferences.errorBackgroundColor\"));\n\n            // Group \"PreferencesPanel\" by group name\n            HashMap<String, ArrayList<PreferencesPanel>> groups = new HashMap<>();\n            ArrayList<String> sortedGroupNames = new ArrayList<>();\n\n            for (PreferencesPanel pp : panels) {\n                ArrayList<PreferencesPanel> pps = groups.get(pp.getPreferencesGroupTitle());\n\n                pp.init(errorBackgroundColor);\n                pp.addPreferencesChangeListener(this);\n\n                if (pps == null) {\n                    String groupNames = pp.getPreferencesGroupTitle();\n                    groups.put(groupNames, pps=new ArrayList<>());\n                    sortedGroupNames.add(groupNames);\n                }\n\n                pps.add(pp);\n            }\n\n            Collections.sort(sortedGroupNames);\n\n            // Add preferences panels\n            for (String groupName : sortedGroupNames) {\n                Box vbox = Box.createVerticalBox();\n                vbox.setBorder(BorderFactory.createTitledBorder(groupName));\n\n                ArrayList<PreferencesPanel> sortedPreferencesPanels = groups.get(groupName);\n                Collections.sort(sortedPreferencesPanels, new PreferencesPanelComparator());\n\n                for (PreferencesPanel pp : sortedPreferencesPanels) {\n                    // Add title\n                    Box hbox = Box.createHorizontalBox();\n                    JLabel title = new JLabel(pp.getPreferencesPanelTitle());\n                    title.setFont(title.getFont().deriveFont(Font.BOLD));\n                    hbox.add(title);\n                    hbox.add(Box.createHorizontalGlue());\n                    hbox.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0));\n                    vbox.add(hbox);\n                    // Add panel\n                    JComponent component = pp.getPanel();\n                    component.setMaximumSize(new Dimension(component.getMaximumSize().width, component.getPreferredSize().height));\n                    vbox.add(component);\n                }\n\n                preferencesPanels.add(vbox);\n            }\n\n            JScrollPane preferencesScrollPane = new JScrollPane(preferencesPanels);\n            preferencesScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);\n            preferencesScrollPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));\n            panel.add(preferencesScrollPane, BorderLayout.CENTER);\n\n            Box vbox = Box.createVerticalBox();\n            panel.add(vbox, BorderLayout.SOUTH);\n\n            vbox.add(Box.createVerticalStrut(15));\n\n            // Buttons \"Ok\" and \"Cancel\"\n            Box hbox = Box.createHorizontalBox();\n            hbox.add(Box.createHorizontalGlue());\n            preferencesOkButton.setText(\"   Ok   \");\n            preferencesOkButton.addActionListener(e -> {\n                for (PreferencesPanel pp : panels) {\n                    pp.savePreferences(preferences);\n                }\n                preferencesDialog.setVisible(false);\n                okCallback.run();\n            });\n            hbox.add(preferencesOkButton);\n            hbox.add(Box.createHorizontalStrut(5));\n            JButton preferencesCancelButton = new JButton(\"Cancel\");\n            Action preferencesCancelActionListener = new AbstractAction() {\n                public void actionPerformed(ActionEvent actionEvent) { preferencesDialog.setVisible(false); }\n            };\n            preferencesCancelButton.addActionListener(preferencesCancelActionListener);\n            hbox.add(preferencesCancelButton);\n            vbox.add(hbox);\n\n            // Last setup\n            JRootPane rootPane = preferencesDialog.getRootPane();\n            rootPane.setDefaultButton(preferencesOkButton);\n            rootPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), \"PreferencesDescription.cancel\");\n            rootPane.getActionMap().put(\"PreferencesDescription.cancel\", preferencesCancelActionListener);\n\n            // Size of the screen\n            Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();\n            // Height of the task bar\n            Insets scnMax = Toolkit.getDefaultToolkit().getScreenInsets(preferencesDialog.getGraphicsConfiguration());\n            // screen height in pixels without taskbar\n            int taskBarHeight = scnMax.bottom + scnMax.top;\n            int maxHeight = screenSize.height - taskBarHeight;\n            int preferredHeight = preferencesPanels.getPreferredSize().height + 2;\n\n            if (preferredHeight > maxHeight) {\n                preferredHeight = maxHeight;\n            }\n\n            preferencesScrollPane.setPreferredSize(new Dimension(400, preferredHeight));\n            preferencesDialog.setMinimumSize(new Dimension(300, 200));\n\n            // Prepare to display\n            preferencesDialog.pack();\n            preferencesDialog.setLocationRelativeTo(mainFrame);\n        });\n    }\n\n    public void show(Runnable okCallback) {\n        this.okCallback = okCallback;\n\n        SwingUtilities.invokeLater(() -> {\n            // Init\n            for (PreferencesPanel pp : panels) {\n                pp.loadPreferences(preferences);\n            }\n            // Show\n            preferencesDialog.setVisible(true);\n        });\n    }\n\n    // --- PreferencesPanel.PreferencesChangeListener --- //\n    public void preferencesPanelChanged(PreferencesPanel source) {\n        SwingUtil.invokeLater(() -> {\n            boolean valid = source.arePreferencesValid();\n\n            valids.put(source, Boolean.valueOf(valid));\n\n            if (valid) {\n                for (PreferencesPanel pp : panels) {\n                    if (valids.get(pp) == Boolean.FALSE) {\n                        preferencesOkButton.setEnabled(false);\n                        return;\n                    }\n                }\n                preferencesOkButton.setEnabled(true);\n            } else {\n                preferencesOkButton.setEnabled(false);\n            }\n        });\n    }\n\n    protected static class PreferencesPanelComparator implements Comparator<PreferencesPanel> {\n        @Override\n        public int compare(PreferencesPanel pp1, PreferencesPanel pp2) {\n            return pp1.getPreferencesPanelTitle().compareTo(pp2.getPreferencesPanelTitle());\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/view/SaveAllSourcesView.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view;\n\nimport org.jd.gui.util.swing.SwingUtil;\n\nimport javax.swing.*;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.KeyEvent;\nimport java.awt.event.WindowAdapter;\nimport java.awt.event.WindowEvent;\nimport java.io.File;\n\npublic class SaveAllSourcesView {\n    protected JDialog saveAllSourcesDialog;\n    protected JLabel saveAllSourcesLabel;\n    protected JProgressBar saveAllSourcesProgressBar;\n\n    public SaveAllSourcesView(JFrame mainFrame, Runnable cancelCallback) {\n        // Build GUI\n        SwingUtil.invokeLater(() -> {\n            saveAllSourcesDialog = new JDialog(mainFrame, \"Save All Sources\", false);\n            saveAllSourcesDialog.setResizable(false);\n            saveAllSourcesDialog.addWindowListener(new WindowAdapter() {\n                @Override public void windowClosing(WindowEvent e) {\n                    cancelCallback.run();\n                }\n            });\n\n            Box vbox = Box.createVerticalBox();\n            vbox.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));\n            saveAllSourcesDialog.add(vbox);\n\n            // First label \"Saving 'file' ...\"\n            Box hbox = Box.createHorizontalBox();\n            hbox.add(saveAllSourcesLabel = new JLabel());\n            hbox.add(Box.createHorizontalGlue());\n            vbox.add(hbox);\n\n            vbox.add(Box.createVerticalStrut(10));\n\n            vbox.add(saveAllSourcesProgressBar = new JProgressBar());\n\n            vbox.add(Box.createVerticalStrut(15));\n\n            // Button \"Cancel\"\n            hbox = Box.createHorizontalBox();\n            hbox.add(Box.createHorizontalGlue());\n            JButton saveAllSourcesCancelButton = new JButton(\"Cancel\");\n            Action saveAllSourcesCancelActionListener = new AbstractAction() {\n                public void actionPerformed(ActionEvent actionEvent) {\n                    cancelCallback.run();\n                    saveAllSourcesDialog.setVisible(false);\n                }\n            };\n            saveAllSourcesCancelButton.addActionListener(saveAllSourcesCancelActionListener);\n            hbox.add(saveAllSourcesCancelButton);\n            vbox.add(hbox);\n\n            // Last setup\n            JRootPane rootPane = saveAllSourcesDialog.getRootPane();\n            rootPane.setDefaultButton(saveAllSourcesCancelButton);\n            rootPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), \"SaveAllSourcesView.cancel\");\n            rootPane.getActionMap().put(\"SaveAllSourcesView.cancel\", saveAllSourcesCancelActionListener);\n\n            // Prepare to display\n            saveAllSourcesDialog.pack();\n        });\n    }\n\n    public void show(File file) {\n        SwingUtil.invokeLater(() -> {\n            // Init\n            saveAllSourcesLabel.setText(\"Saving '\" + file.getAbsolutePath() + \"'...\");\n            saveAllSourcesProgressBar.setValue(0);\n            saveAllSourcesProgressBar.setMaximum(10);\n            saveAllSourcesProgressBar.setIndeterminate(true);\n            saveAllSourcesDialog.pack();\n            // Show\n            saveAllSourcesDialog.setLocationRelativeTo(saveAllSourcesDialog.getParent());\n            saveAllSourcesDialog.setVisible(true);\n        });\n    }\n\n    public boolean isVisible() { return saveAllSourcesDialog.isVisible(); }\n\n    public void setMaxValue(int maxValue) {\n        SwingUtil.invokeLater(() -> {\n            if (maxValue > 0) {\n                saveAllSourcesProgressBar.setMaximum(maxValue);\n                saveAllSourcesProgressBar.setIndeterminate(false);\n            } else {\n                saveAllSourcesProgressBar.setIndeterminate(true);\n            }\n        });\n    }\n\n    public void updateProgressBar(int value) {\n        SwingUtil.invokeLater(() -> {\n            saveAllSourcesProgressBar.setValue(value);\n        });\n    }\n\n    public void hide() {\n        SwingUtil.invokeLater(() -> {\n            saveAllSourcesDialog.setVisible(false);\n        });\n    }\n\n    public void showActionFailedDialog() {\n        SwingUtil.invokeLater(() -> {\n            JOptionPane.showMessageDialog(saveAllSourcesDialog, \"'Save All Sources' action failed.\", \"Error\", JOptionPane.ERROR_MESSAGE);\n        });\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/view/SearchInConstantPoolsView.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.TreeNodeExpandable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.model.container.DelegatingFilterContainer;\nimport org.jd.gui.spi.TreeNodeFactory;\nimport org.jd.gui.util.function.TriConsumer;\nimport org.jd.gui.util.swing.SwingUtil;\nimport org.jd.gui.view.component.Tree;\nimport org.jd.gui.view.renderer.TreeNodeRenderer;\n\nimport javax.swing.*;\nimport javax.swing.event.DocumentEvent;\nimport javax.swing.event.DocumentListener;\nimport javax.swing.event.TreeExpansionEvent;\nimport javax.swing.event.TreeExpansionListener;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport javax.swing.tree.DefaultTreeModel;\nimport javax.swing.tree.TreePath;\nimport java.awt.*;\nimport java.awt.event.*;\nimport java.net.URI;\nimport java.util.*;\nimport java.util.function.BiConsumer;\n\npublic class SearchInConstantPoolsView<T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> {\n    protected static final ContainerComparator CONTAINER_COMPARATOR = new ContainerComparator();\n\n    public static final int SEARCH_TYPE = 1;\n    public static final int SEARCH_CONSTRUCTOR = 2;\n    public static final int SEARCH_METHOD = 4;\n    public static final int SEARCH_FIELD = 8;\n    public static final int SEARCH_STRING = 16;\n    public static final int SEARCH_MODULE = 32;\n    public static final int SEARCH_DECLARATION = 64;\n    public static final int SEARCH_REFERENCE = 128;\n\n    protected API api;\n    protected Set<URI> accepted = new HashSet<>();\n    protected Set<URI> expanded = new HashSet<>();\n\n    protected JDialog searchInConstantPoolsDialog;\n    protected JTextField searchInConstantPoolsEnterTextField;\n    protected JLabel searchInConstantPoolsLabel;\n    protected JCheckBox searchInConstantPoolsCheckBoxType;\n    protected JCheckBox searchInConstantPoolsCheckBoxField;\n    protected JCheckBox searchInConstantPoolsCheckBoxConstructor;\n    protected JCheckBox searchInConstantPoolsCheckBoxMethod;\n    protected JCheckBox searchInConstantPoolsCheckBoxString;\n    protected JCheckBox searchInConstantPoolsCheckBoxModule;\n    protected JCheckBox searchInConstantPoolsCheckBoxDeclarations;\n    protected JCheckBox searchInConstantPoolsCheckBoxReferences;\n    protected Tree searchInConstantPoolsTree;\n\n    @SuppressWarnings(\"unchecked\")\n    public SearchInConstantPoolsView(\n            API api, JFrame mainFrame,\n            BiConsumer<String, Integer> changedPatternCallback,\n            TriConsumer<URI, String, Integer> selectedTypeCallback) {\n        this.api = api;\n        // Build GUI\n        SwingUtil.invokeLater(() -> {\n            searchInConstantPoolsDialog = new JDialog(mainFrame, \"Search\", false);\n\n            JPanel panel = new JPanel();\n            panel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));\n            panel.setLayout(new BorderLayout());\n            searchInConstantPoolsDialog.add(panel);\n\n            // Box for search criteria\n            Box vbox = Box.createVerticalBox();\n\n            Box hbox = Box.createHorizontalBox();\n            hbox.add(new JLabel(\"Search string (* = any string, ? = any character):\"));\n            hbox.add(Box.createHorizontalGlue());\n            vbox.add(hbox);\n\n            vbox.add(Box.createVerticalStrut(10));\n\n            // Text field\n            vbox.add(searchInConstantPoolsEnterTextField = new JTextField(30));\n            searchInConstantPoolsEnterTextField.addKeyListener(new KeyAdapter() {\n                @Override public void keyTyped(KeyEvent e)  {\n                    switch (e.getKeyChar()) {\n                        case '=': case '(': case ')': case '{': case '}': case '[': case ']':\n                            e.consume();\n                            break;\n                        default:\n                            if (Character.isDigit(e.getKeyChar()) && (searchInConstantPoolsEnterTextField.getText().length() == 0)) {\n                                // First character can not be a digit\n                                e.consume();\n                            }\n                            break;\n                    }\n                }\n                @Override public void keyPressed(KeyEvent e) {\n                    if (e.getKeyCode() == KeyEvent.VK_DOWN) {\n                        DefaultMutableTreeNode root = (DefaultMutableTreeNode)searchInConstantPoolsTree.getModel().getRoot();\n                        if (root.getChildCount() > 0) {\n                            searchInConstantPoolsTree.requestFocus();\n                            if (searchInConstantPoolsTree.getSelectionCount() == 0) {\n                                searchInConstantPoolsTree.setSelectionPath(new TreePath(((DefaultMutableTreeNode)root.getChildAt(0)).getPath()));\n                            }\n                            e.consume();\n                        }\n                    }\n                }\n            });\n            searchInConstantPoolsEnterTextField.getDocument().addDocumentListener(new DocumentListener() {\n                @Override public void insertUpdate(DocumentEvent e) { call(); }\n                @Override public void removeUpdate(DocumentEvent e) { call(); }\n                @Override public void changedUpdate(DocumentEvent e) { call(); }\n                protected void call() { changedPatternCallback.accept(searchInConstantPoolsEnterTextField.getText(), getFlags()); }\n            });\n\n            vbox.add(Box.createVerticalStrut(10));\n\n            hbox = Box.createHorizontalBox();\n            vbox.add(hbox);\n\n            JPanel subpanel = new JPanel();\n            subpanel.setBorder(BorderFactory.createTitledBorder(\"Search For\"));\n            subpanel.setLayout(new BorderLayout());\n            hbox.add(subpanel);\n\n            Box subhbox = Box.createHorizontalBox();\n            subpanel.add(subhbox, BorderLayout.WEST);\n\n            ItemListener checkBoxListener = (e) -> {\n                changedPatternCallback.accept(searchInConstantPoolsEnterTextField.getText(), getFlags());\n                searchInConstantPoolsEnterTextField.requestFocus();\n            };\n\n            JPanel subsubpanel = new JPanel();\n            subsubpanel.setLayout(new GridLayout(2, 1));\n            subsubpanel.add(searchInConstantPoolsCheckBoxType = new JCheckBox(\"Type\", true));\n            searchInConstantPoolsCheckBoxType.addItemListener(checkBoxListener);\n            subsubpanel.add(searchInConstantPoolsCheckBoxField = new JCheckBox(\"Field\"));\n            searchInConstantPoolsCheckBoxField.addItemListener(checkBoxListener);\n            subhbox.add(subsubpanel);\n\n            subsubpanel = new JPanel();\n            subsubpanel.setLayout(new GridLayout(2, 1));\n            subsubpanel.add(searchInConstantPoolsCheckBoxConstructor = new JCheckBox(\"Constructor\"));\n            searchInConstantPoolsCheckBoxConstructor.addItemListener(checkBoxListener);\n            subsubpanel.add(searchInConstantPoolsCheckBoxMethod = new JCheckBox(\"Method\"));\n            searchInConstantPoolsCheckBoxMethod.addItemListener(checkBoxListener);\n            subhbox.add(subsubpanel);\n\n            subsubpanel = new JPanel();\n            subsubpanel.setLayout(new GridLayout(2, 1));\n            subsubpanel.add(searchInConstantPoolsCheckBoxString = new JCheckBox(\"String Constant\"));\n            searchInConstantPoolsCheckBoxString.addItemListener(checkBoxListener);\n            subsubpanel.add(searchInConstantPoolsCheckBoxModule = new JCheckBox(\"Java Module\"));\n            searchInConstantPoolsCheckBoxModule.addItemListener(checkBoxListener);\n            subhbox.add(subsubpanel);\n\n            subpanel = new JPanel();\n            subpanel.setBorder(BorderFactory.createTitledBorder(\"Limit To\"));\n            subpanel.setLayout(new BorderLayout());\n            hbox.add(subpanel);\n\n            subhbox = Box.createHorizontalBox();\n            subpanel.add(subhbox, BorderLayout.WEST);\n\n            subsubpanel = new JPanel();\n            subsubpanel.setLayout(new GridLayout(2, 1));\n            subsubpanel.add(searchInConstantPoolsCheckBoxDeclarations = new JCheckBox(\"Declarations\", true));\n            searchInConstantPoolsCheckBoxDeclarations.addItemListener(checkBoxListener);\n            subsubpanel.add(searchInConstantPoolsCheckBoxReferences = new JCheckBox(\"References\", true));\n            searchInConstantPoolsCheckBoxReferences.addItemListener(checkBoxListener);\n            subhbox.add(subsubpanel);\n\n            vbox.add(Box.createVerticalStrut(10));\n\n            hbox = Box.createHorizontalBox();\n            hbox.add(searchInConstantPoolsLabel = new JLabel(\"Matching types:\"));\n            hbox.add(Box.createHorizontalGlue());\n            vbox.add(hbox);\n\n            vbox.add(Box.createVerticalStrut(10));\n            panel.add(vbox, BorderLayout.NORTH);\n\n            JScrollPane scrollPane = new JScrollPane(searchInConstantPoolsTree = new Tree());\n            searchInConstantPoolsTree.setShowsRootHandles(true);\n            searchInConstantPoolsTree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode()));\n            searchInConstantPoolsTree.setCellRenderer(new TreeNodeRenderer());\n            searchInConstantPoolsTree.addKeyListener(new KeyAdapter() {\n                @Override public void keyPressed(KeyEvent e) {\n                    if (e.getKeyCode() == KeyEvent.VK_UP) {\n                        if (searchInConstantPoolsTree.getLeadSelectionRow() == 0) {\n                            searchInConstantPoolsEnterTextField.requestFocus();\n                            e.consume();\n                        }\n                    }\n                }\n            });\n            searchInConstantPoolsTree.addMouseListener(new MouseAdapter() {\n                @Override public void mouseClicked(MouseEvent e) {\n                    if (e.getClickCount() == 2) {\n                        T node = (T)searchInConstantPoolsTree.getLastSelectedPathComponent();\n                        if (node != null) {\n                            selectedTypeCallback.accept(node.getUri(), searchInConstantPoolsEnterTextField.getText(), getFlags());\n                        }\n                    }\n                }\n            });\n            searchInConstantPoolsTree.addTreeExpansionListener(new TreeExpansionListener() {\n                @Override public void treeExpanded(TreeExpansionEvent e) {\n                    DefaultTreeModel model = (DefaultTreeModel)searchInConstantPoolsTree.getModel();\n                    T node = (T)e.getPath().getLastPathComponent();\n                    // Expand node and find the first leaf\n                    while (true) {\n                        populate(model, node);\n                        if (node.getChildCount() == 0) {\n                            break;\n                        }\n                        node = (T)node.getChildAt(0);\n                    }\n                    searchInConstantPoolsTree.setSelectionPath(new TreePath(node.getPath()));\n                }\n                @Override public void treeCollapsed(TreeExpansionEvent e) {}\n            });\n            scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);\n            scrollPane.setPreferredSize(new Dimension(400, 150));\n            panel.add(scrollPane, BorderLayout.CENTER);\n\n            vbox = Box.createVerticalBox();\n\n            vbox.add(Box.createVerticalStrut(25));\n\n            hbox = Box.createHorizontalBox();\n            hbox.add(Box.createHorizontalGlue());\n            JButton searchInConstantPoolsOpenButton = new JButton(\"Open\");\n            hbox.add(searchInConstantPoolsOpenButton);\n            searchInConstantPoolsOpenButton.setEnabled(false);\n            Action searchInConstantPoolsOpenActionListener = new AbstractAction() {\n                @Override public void actionPerformed(ActionEvent actionEvent) {\n                    T selectedTreeNode = (T)searchInConstantPoolsTree.getLastSelectedPathComponent();\n                    if (selectedTreeNode != null) {\n                        selectedTypeCallback.accept(selectedTreeNode.getUri(), searchInConstantPoolsEnterTextField.getText(), getFlags());\n                    }\n                }\n            };\n            searchInConstantPoolsOpenButton.addActionListener(searchInConstantPoolsOpenActionListener);\n            hbox.add(Box.createHorizontalStrut(5));\n            JButton searchInConstantPoolsCancelButton = new JButton(\"Cancel\");\n            hbox.add(searchInConstantPoolsCancelButton);\n            Action searchInConstantPoolsCancelActionListener = new AbstractAction() {\n                @Override public void actionPerformed(ActionEvent actionEvent) { searchInConstantPoolsDialog.setVisible(false); }\n            };\n            searchInConstantPoolsCancelButton.addActionListener(searchInConstantPoolsCancelActionListener);\n\n            vbox.add(hbox);\n\n            panel.add(vbox, BorderLayout.SOUTH);\n\n            // Last setup\n            JRootPane rootPane = searchInConstantPoolsDialog.getRootPane();\n            rootPane.setDefaultButton(searchInConstantPoolsOpenButton);\n            rootPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), \"SearchInConstantPoolsView.cancel\");\n            rootPane.getActionMap().put(\"SearchInConstantPoolsView.cancel\", searchInConstantPoolsCancelActionListener);\n\n            searchInConstantPoolsDialog.setMinimumSize(searchInConstantPoolsDialog.getSize());\n\n            searchInConstantPoolsEnterTextField.addFocusListener(new FocusAdapter() {\n                @Override public void focusGained(FocusEvent e) {\n                    searchInConstantPoolsTree.clearSelection();\n                    searchInConstantPoolsOpenButton.setEnabled(false);\n                }\n            });\n\n            searchInConstantPoolsTree.addFocusListener(new FocusAdapter() {\n                @Override public void focusGained(FocusEvent e) {\n                    searchInConstantPoolsOpenButton.setEnabled(searchInConstantPoolsTree.getSelectionCount() > 0);\n                }\n            });\n\n            // Prepare to display\n            searchInConstantPoolsDialog.pack();\n            searchInConstantPoolsDialog.setLocationRelativeTo(searchInConstantPoolsDialog.getParent());\n        });\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected void populate(DefaultTreeModel model, T node) {\n        // Populate node\n        populate(node);\n        // Populate children\n        int i = node.getChildCount();\n        while (i-- > 0) {\n            T child = (T)node.getChildAt(i);\n            if ((child instanceof TreeNodeExpandable) && !expanded.contains(child.getUri())) {\n                populate(child);\n            }\n        }\n        // Refresh\n        model.reload(node);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected void populate(T node) {\n        if ((node instanceof TreeNodeExpandable) && !expanded.contains(node.getUri())) {\n            // Populate\n            ((TreeNodeExpandable)node).populateTreeNode(api);\n            expanded.add(node.getUri());\n            // Filter\n            int i = node.getChildCount();\n            while (i-- > 0) {\n                if (!accepted.contains(((T)node.getChildAt(i)).getUri())) {\n                    node.remove(i);\n                }\n            }\n        }\n    }\n\n    public void show() {\n        SwingUtil.invokeLater(() -> {\n            searchInConstantPoolsEnterTextField.selectAll();\n            // Show\n            searchInConstantPoolsDialog.setVisible(true);\n            searchInConstantPoolsEnterTextField.requestFocus();\n        });\n    }\n\n    public boolean isVisible() { return searchInConstantPoolsDialog.isVisible(); }\n\n    public String getPattern() { return searchInConstantPoolsEnterTextField.getText(); }\n\n    public int getFlags() {\n        int flags = 0;\n\n        if (searchInConstantPoolsCheckBoxType.isSelected())\n            flags += SEARCH_TYPE;\n        if (searchInConstantPoolsCheckBoxConstructor.isSelected())\n            flags += SEARCH_CONSTRUCTOR;\n        if (searchInConstantPoolsCheckBoxMethod.isSelected())\n            flags += SEARCH_METHOD;\n        if (searchInConstantPoolsCheckBoxField.isSelected())\n            flags += SEARCH_FIELD;\n        if (searchInConstantPoolsCheckBoxString.isSelected())\n            flags += SEARCH_STRING;\n        if (searchInConstantPoolsCheckBoxModule.isSelected())\n            flags += SEARCH_MODULE;\n        if (searchInConstantPoolsCheckBoxDeclarations.isSelected())\n            flags += SEARCH_DECLARATION;\n        if (searchInConstantPoolsCheckBoxReferences.isSelected())\n            flags += SEARCH_REFERENCE;\n\n        return flags;\n    }\n\n    public void showWaitCursor() {\n        SwingUtil.invokeLater(() -> searchInConstantPoolsDialog.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)));\n    }\n\n    public void hideWaitCursor() {\n        SwingUtil.invokeLater(() -> searchInConstantPoolsDialog.setCursor(Cursor.getDefaultCursor()));\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public void updateTree(Collection<DelegatingFilterContainer> containers, int matchingTypeCount) {\n        SwingUtil.invokeLater(() -> {\n            DefaultTreeModel model = (DefaultTreeModel)searchInConstantPoolsTree.getModel();\n            T root = (T)model.getRoot();\n\n            // Reset tree nodes\n            root.removeAllChildren();\n\n            accepted.clear();\n            expanded.clear();\n\n            if (containers != null) {\n                ArrayList<DelegatingFilterContainer> list = new ArrayList<>(containers);\n\n                list.sort(CONTAINER_COMPARATOR);\n\n                for (DelegatingFilterContainer container : list) {\n                    // Init uri set\n                    accepted.addAll(container.getUris());\n                    // Populate tree\n                    Container.Entry parentEntry = container.getRoot().getParent();\n                    TreeNodeFactory treeNodeFactory = api.getTreeNodeFactory(parentEntry);\n\n                    if (treeNodeFactory != null) {\n                        root.add(treeNodeFactory.make(api, parentEntry));\n                    }\n                }\n\n                // Expand node and find the first leaf\n                T node = root;\n                while (true) {\n                    populate(model, node);\n                    if (node.getChildCount() == 0) {\n                        break;\n                    }\n                    node = (T)node.getChildAt(0);\n                }\n                searchInConstantPoolsTree.setSelectionPath(new TreePath(node.getPath()));\n            } else {\n                model.reload();\n            }\n\n            // Update matching item counter\n            switch (matchingTypeCount) {\n                case 0:\n                    searchInConstantPoolsLabel.setText(\"Matching entries:\");\n                    break;\n                case 1:\n                    searchInConstantPoolsLabel.setText(\"1 matching entry:\");\n                    break;\n                default:\n                    searchInConstantPoolsLabel.setText(matchingTypeCount + \" matching entries:\");\n            }\n        });\n    }\n\n    protected static class ContainerComparator implements Comparator<Container> {\n        @Override\n        public int compare(Container c1, Container c2) {\n            return c1.getRoot().getUri().compareTo(c2.getRoot().getUri());\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/view/SelectLocationView.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.TreeNodeExpandable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.model.container.DelegatingFilterContainer;\nimport org.jd.gui.spi.TreeNodeFactory;\nimport org.jd.gui.util.swing.SwingUtil;\nimport org.jd.gui.view.component.Tree;\nimport org.jd.gui.view.renderer.TreeNodeRenderer;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport javax.swing.tree.DefaultTreeModel;\nimport javax.swing.tree.TreePath;\nimport java.awt.*;\nimport java.awt.event.*;\nimport java.net.URI;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Comparator;\nimport java.util.Set;\nimport java.util.function.Consumer;\n\npublic class SelectLocationView<T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> {\n    protected static final DelegatingFilterContainerComparator DELEGATING_FILTER_CONTAINER_COMPARATOR = new DelegatingFilterContainerComparator();\n\n    protected API api;\n\n    protected JDialog selectLocationDialog;\n    protected JLabel selectLocationLabel;\n    protected Tree selectLocationTree;\n\n    protected Consumer<URI> selectedEntryCallback;\n    protected Runnable closeCallback;\n\n    @SuppressWarnings(\"unchecked\")\n    public SelectLocationView(API api, JFrame mainFrame) {\n        this.api = api;\n        // Build GUI\n        SwingUtil.invokeLater(() -> {\n            selectLocationDialog = new JDialog(mainFrame, \"\", false);\n            selectLocationDialog.setUndecorated(true);\n            selectLocationDialog.addWindowListener(new WindowAdapter() {\n                @Override public void windowDeactivated(WindowEvent e) { closeCallback.run(); }\n            });\n\n            Color bg = UIManager.getColor(\"ToolTip.background\");\n\n            JPanel selectLocationPanel = new JPanel(new BorderLayout());\n            selectLocationPanel.setBorder(BorderFactory.createLineBorder(bg.darker()));\n            selectLocationPanel.setBackground(bg);\n            selectLocationDialog.add(selectLocationPanel);\n\n            selectLocationLabel = new JLabel();\n            selectLocationLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));\n            selectLocationPanel.add(selectLocationLabel, BorderLayout.NORTH);\n\n            selectLocationTree = new Tree();\n            selectLocationTree.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));\n            selectLocationTree.setOpaque(false);\n            selectLocationTree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode()));\n            selectLocationTree.setCellRenderer(new TreeNodeRenderer());\n            selectLocationTree.addKeyListener(new KeyAdapter() {\n                @Override public void keyPressed(KeyEvent e) {\n                    if (e.getKeyCode() == KeyEvent.VK_ENTER) {\n                        onSelectedEntry();\n                    }\n                }\n            });\n            selectLocationTree.addMouseListener(new MouseAdapter() {\n                @Override public void mouseClicked(MouseEvent e) {\n                    if (e.getClickCount() > 0) {\n                        onSelectedEntry();\n                    }\n                }\n            });\n            selectLocationTree.addFocusListener(new FocusAdapter() {\n                @Override public void focusLost(FocusEvent e) { selectLocationDialog.setVisible(false); }\n            });\n            selectLocationPanel.add(selectLocationTree, BorderLayout.CENTER);\n\n            // Last setup\n            JRootPane rootPane = selectLocationDialog.getRootPane();\n            rootPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), \"SelectLocationView.cancel\");\n            rootPane.getActionMap().put(\"SelectLocationView.cancel\", new AbstractAction() {\n                @Override public void actionPerformed(ActionEvent e) { selectLocationDialog.setVisible(false); }\n            });\n        });\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public void show(Point location, Collection<DelegatingFilterContainer> containers, int locationCount, Consumer<URI> selectedEntryCallback, Runnable closeCallback) {\n        this.selectedEntryCallback = selectedEntryCallback;\n        this.closeCallback = closeCallback;\n\n        SwingUtil.invokeLater(() -> {\n            // Init\n            T root = (T)selectLocationTree.getModel().getRoot();\n\n            // Reset tree nodes\n            root.removeAllChildren();\n\n            ArrayList<DelegatingFilterContainer> sortedContainers = new ArrayList<>(containers);\n            sortedContainers.sort(DELEGATING_FILTER_CONTAINER_COMPARATOR);\n\n            for (DelegatingFilterContainer container : sortedContainers) {\n                Container.Entry parentEntry = container.getRoot().getParent();\n                TreeNodeFactory factory = api.getTreeNodeFactory(parentEntry);\n\n                if (factory != null) {\n                    T node = factory.make(api, parentEntry);\n\n                    if (node != null) {\n                        root.add(node);\n                        populate(container.getUris(), node);\n                    }\n                }\n            }\n\n            ((DefaultTreeModel)selectLocationTree.getModel()).reload();\n\n            // Expand all nodes\n            for (int row = 0; row < selectLocationTree.getRowCount(); row++) {\n                selectLocationTree.expandRow(row);\n            }\n\n            // Select first leaf\n            T node = root;\n            while (true) {\n                if (node.getChildCount() == 0) {\n                    break;\n                }\n                node = (T)node.getChildAt(0);\n            }\n            selectLocationTree.setSelectionPath(new TreePath(node.getPath()));\n\n            // Reset preferred size\n            selectLocationTree.setPreferredSize(null);\n\n            // Resize\n            Dimension ps = selectLocationTree.getPreferredSize();\n            if (ps.width < 200)\n                ps.width = 200;\n            if (ps.height < 50)\n                ps.height = 50;\n            selectLocationTree.setPreferredSize(ps);\n\n            selectLocationLabel.setText(\"\" + locationCount + \" locations:\");\n\n            selectLocationDialog.pack();\n            selectLocationDialog.setLocation(location);\n            // Show\n            selectLocationDialog.setVisible(true);\n            selectLocationTree.requestFocus();\n        });\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected void populate(Set<URI> uris, DefaultMutableTreeNode node) {\n        if (node instanceof TreeNodeExpandable) {\n            ((TreeNodeExpandable)node).populateTreeNode(api);\n\n            int i = node.getChildCount();\n\n            while (i-- > 0) {\n                T child = (T)node.getChildAt(i);\n\n                if (uris.contains(child.getUri())) {\n                    populate(uris, child);\n                } else {\n                    node.remove(i);\n                }\n            }\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected void onSelectedEntry() {\n        T node = (T)selectLocationTree.getLastSelectedPathComponent();\n\n        if (node != null) {\n            selectLocationDialog.setVisible(false);\n            selectedEntryCallback.accept(node.getUri());\n        }\n    }\n\n    protected static class DelegatingFilterContainerComparator implements Comparator<DelegatingFilterContainer> {\n        @Override\n        public int compare(DelegatingFilterContainer fcw1, DelegatingFilterContainer fcw2) {\n            return fcw1.getRoot().getUri().compareTo(fcw2.getRoot().getUri());\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/view/bean/OpenTypeListCellBean.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view.bean;\n\nimport org.jd.gui.api.model.Container;\n\nimport javax.swing.*;\nimport java.util.Collection;\n\npublic class OpenTypeListCellBean {\n    public String label;\n    public String packag;\n    public Icon icon;\n    public Collection<Container.Entry> entries;\n    public String typeName;\n\n    public OpenTypeListCellBean(String label, Collection<Container.Entry> entries, String typeName) {\n        this.label = label;\n        this.entries = entries;\n        this.typeName = typeName;\n    }\n\n    public OpenTypeListCellBean(String label, String packag, Icon icon, Collection<Container.Entry> entries, String typeName) {\n        this.label = label;\n        this.packag = packag;\n        this.icon = icon;\n        this.entries = entries;\n        this.typeName = typeName;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/view/component/IconButton.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view.component;\n\nimport javax.swing.*;\nimport java.awt.*;\n\npublic class IconButton extends JButton {\n\tprotected static final Insets INSETS0 = new Insets(0, 0, 0, 0);\n\n\tpublic IconButton(String text, Action action) {\n\t\tsetFocusPainted(false);\n\t\tsetBorderPainted(false);\n\t\tsetMargin(INSETS0);\n\t\tsetAction(action);\n\t\tsetText(text);\n\t}\n\n\tpublic IconButton(Action action) {\n\t\tthis(null, action);\n\t}\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/view/component/List.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view.component;\n\nimport org.jd.gui.api.model.TreeNodeData;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.awt.*;\nimport java.awt.event.KeyEvent;\n\npublic class List extends JList {\n\n    @SuppressWarnings(\"unchecked\")\n    public List() {\n        super();\n\n        Toolkit toolkit = Toolkit.getDefaultToolkit();\n        KeyStroke ctrlA = KeyStroke.getKeyStroke(KeyEvent.VK_A, toolkit.getMenuShortcutKeyMask());\n        KeyStroke ctrlC = KeyStroke.getKeyStroke(KeyEvent.VK_C, toolkit.getMenuShortcutKeyMask());\n        KeyStroke ctrlV = KeyStroke.getKeyStroke(KeyEvent.VK_V, toolkit.getMenuShortcutKeyMask());\n\n        InputMap inputMap = getInputMap();\n        inputMap.put(ctrlA, \"none\");\n        inputMap.put(ctrlC, \"none\");\n        inputMap.put(ctrlV, \"none\");\n\n        setCellRenderer(new Renderer());\n    }\n\n    protected class Renderer implements ListCellRenderer {\n        protected Color textSelectionColor;\n        protected Color backgroundSelectionColor;\n        protected Color textNonSelectionColor;\n        protected Color backgroundNonSelectionColor;\n\n        protected JLabel label;\n\n        public Renderer() {\n            label = new JLabel();\n            label.setOpaque(true);\n\n            textSelectionColor = UIManager.getColor(\"List.dropCellForeground\");\n            backgroundSelectionColor = UIManager.getColor(\"List.dropCellBackground\");\n            textNonSelectionColor = UIManager.getColor(\"List.foreground\");\n            backgroundNonSelectionColor = UIManager.getColor(\"List.background\");\n            Insets margins = UIManager.getInsets(\"List.contentMargins\");\n\n            if (textSelectionColor == null)\n                textSelectionColor = List.this.getSelectionForeground();\n            if (backgroundSelectionColor == null)\n                backgroundSelectionColor = List.this.getSelectionBackground();\n\n            if (margins != null) {\n                label.setBorder(BorderFactory.createEmptyBorder(margins.top, margins.left, margins.bottom, margins.right));\n            } else {\n                label.setBorder(BorderFactory.createEmptyBorder(0, 2, 1, 2));\n            }\n        }\n\n        @Override\n        public Component getListCellRendererComponent(JList list, Object value, int index, boolean selected, boolean hasFocus) {\n            Object data = ((DefaultMutableTreeNode)value).getUserObject();\n\n            if (data instanceof TreeNodeData) {\n                TreeNodeData tnd = (TreeNodeData)data;\n                label.setIcon(tnd.getIcon());\n                label.setText(tnd.getLabel());\n            } else {\n                label.setIcon(null);\n                label.setText(\"\" + data);\n            }\n\n            if (selected) {\n                label.setForeground(textSelectionColor);\n                label.setBackground(backgroundSelectionColor);\n            } else {\n                label.setForeground(textNonSelectionColor);\n                label.setBackground(backgroundNonSelectionColor);\n            }\n\n            return label;\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/view/component/Tree.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view.component;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.awt.event.KeyEvent;\n\npublic class Tree extends JTree {\n    public Tree() {\n        Toolkit toolkit = Toolkit.getDefaultToolkit();\n        KeyStroke ctrlA = KeyStroke.getKeyStroke(KeyEvent.VK_A, toolkit.getMenuShortcutKeyMask());\n        KeyStroke ctrlC = KeyStroke.getKeyStroke(KeyEvent.VK_C, toolkit.getMenuShortcutKeyMask());\n        KeyStroke ctrlV = KeyStroke.getKeyStroke(KeyEvent.VK_V, toolkit.getMenuShortcutKeyMask());\n\n        InputMap inputMap = getInputMap();\n        inputMap.put(ctrlA, \"none\");\n        inputMap.put(ctrlC, \"none\");\n        inputMap.put(ctrlV, \"none\");\n\n        setRootVisible(false);\n    }\n\n    public void fireVisibleDataPropertyChange() {\n        if (getAccessibleContext() != null) {\n            getAccessibleContext().firePropertyChange(\"AccessibleVisibleData\", false, true);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/view/component/panel/MainTabbedPanel.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view.component.panel;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.*;\nimport org.jd.gui.service.platform.PlatformService;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.net.URI;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\n@SuppressWarnings(\"unchecked\")\npublic class MainTabbedPanel<T extends JComponent & UriGettable> extends TabbedPanel<T> implements UriOpenable, PreferencesChangeListener, PageChangeListener {\n    protected ArrayList<PageChangeListener> pageChangedListeners = new ArrayList<>();\n    // Flag to prevent the event cascades\n    protected boolean pageChangedListenersEnabled = true;\n\n    public MainTabbedPanel(API api) {\n        super(api);\n    }\n\n    @Override\n    public void create() {\n        setLayout(cardLayout = new CardLayout());\n\n        Color bg = darker(getBackground());\n\n        if (PlatformService.getInstance().isWindows()) {\n            setBackground(bg);\n        }\n\n        // panel //\n        JPanel panel = new JPanel();\n        panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));\n        panel.setBackground(bg);\n\n        Color fontColor = panel.getBackground().darker();\n\n        panel.add(Box.createHorizontalGlue());\n\n        JPanel box = new JPanel();\n        box.setLayout(new BoxLayout(box, BoxLayout.Y_AXIS));\n        box.setBackground(panel.getBackground());\n        box.add(Box.createVerticalGlue());\n\n        JLabel title = newLabel(\"No files are open\", fontColor);\n        title.setFont(title.getFont().deriveFont(Font.BOLD, title.getFont().getSize()+8));\n\n        box.add(title);\n        box.add(newLabel(\"Open a file with menu \\\"File > Open File...\\\"\", fontColor));\n        box.add(newLabel(\"Open recent files with menu \\\"File > Recent Files\\\"\", fontColor));\n        box.add(newLabel(\"Drag and drop files from \" + getFileManagerLabel(), fontColor));\n        box.add(Box.createVerticalGlue());\n\n        panel.add(box);\n        panel.add(Box.createHorizontalGlue());\n        add(\"panel\", panel);\n\n        // tabs //\n        tabbedPane = createTabPanel();\n        tabbedPane.addChangeListener(e -> {\n            if (pageChangedListenersEnabled) {\n                JComponent subPage = (JComponent)tabbedPane.getSelectedComponent();\n\n                if (subPage == null) {\n                    // Fire page changed event\n                    for (PageChangeListener listener : pageChangedListeners) {\n                        listener.pageChanged(null);\n                    }\n                } else {\n                    T page = (T)subPage.getClientProperty(\"currentPage\");\n\n                    if (page == null) {\n                        page = (T)tabbedPane.getSelectedComponent();\n                    }\n                    // Fire page changed event\n                    for (PageChangeListener listener : pageChangedListeners) {\n                        listener.pageChanged(page);\n                    }\n                    // Update current sub-page preferences\n                    if (subPage instanceof PreferencesChangeListener) {\n                        ((PreferencesChangeListener)subPage).preferencesChanged(preferences);\n                    }\n                }\n            }\n        });\n\t\tadd(\"tabs\", tabbedPane);\n\n\t\tsetBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, darker(darker(bg))));\n\t}\n\n\tprotected String getFileManagerLabel() {\n        switch (PlatformService.getInstance().getOs()) {\n            case Linux:\n                return \"your file manager\";\n            case MacOSX:\n                return \"the Finder\";\n            default:\n                return \"Explorer\";\n        }\n    }\n\n\tprotected JLabel newLabel(String text, Color fontColor) {\n        JLabel label = new JLabel(text);\n        label.setForeground(fontColor);\n        return label;\n    }\n\n    @Override\n    public void addPage(String title, Icon icon, String tip, T page) {\n        super.addPage(title, icon, tip, page);\n        if (page instanceof PageChangeable) {\n            ((PageChangeable)page).addPageChangeListener(this);\n        }\n    }\n\n    public List<T> getPages() {\n        int i = tabbedPane.getTabCount();\n        ArrayList<T> pages = new ArrayList<>(i);\n        while (i-- > 0) {\n            pages.add((T)tabbedPane.getComponentAt(i));\n        }\n        return pages;\n    }\n\n    public ArrayList<PageChangeListener> getPageChangedListeners() {\n        return pageChangedListeners;\n    }\n\n    // --- URIOpener --- //\n    @Override\n    public boolean openUri(URI uri) {\n        try {\n            // Disable page changed event\n            pageChangedListenersEnabled = false;\n            // Search & display main tab\n            T page = showPage(uri);\n\n            if (page != null) {\n                if (page instanceof UriOpenable) {\n                    // Enable page changed event\n                    pageChangedListenersEnabled = true;\n                    // Search & display sub tab\n                    return ((UriOpenable)page).openUri(uri);\n                }\n                return true;\n            }\n        } finally {\n            // Enable page changed event\n            pageChangedListenersEnabled = true;\n        }\n\n        return false;\n    }\n\n    // --- PageChangedListener --- //\n    @Override\n    public <T extends JComponent & UriGettable> void pageChanged(T page) {\n        // Store active page for current sub tabbed pane\n        Component subPage = tabbedPane.getSelectedComponent();\n\n        if (subPage != null) {\n            ((JComponent)subPage).putClientProperty(\"currentPage\", page);\n        }\n\n        if (page == null) {\n            page = (T)subPage;\n        }\n\n        // Forward event\n        for (PageChangeListener listener : pageChangedListeners) {\n            listener.pageChanged(page);\n        }\n    }\n\n    // --- PreferencesChangeListener --- //\n    @Override\n    public void preferencesChanged(Map<String, String> preferences) {\n        super.preferencesChanged(preferences);\n\n        // Update current sub-page preferences\n        Component subPage = tabbedPane.getSelectedComponent();\n        if (subPage instanceof PreferencesChangeListener) {\n            ((PreferencesChangeListener)subPage).preferencesChanged(preferences);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/view/component/panel/TabbedPanel.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view.component.panel;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.PreferencesChangeListener;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.service.platform.PlatformService;\n\nimport javax.swing.*;\nimport javax.swing.event.ChangeEvent;\nimport javax.swing.event.ChangeListener;\nimport java.awt.*;\nimport java.awt.event.*;\nimport java.net.URI;\nimport java.util.Collection;\nimport java.util.Map;\n\npublic class TabbedPanel<T extends JComponent & UriGettable> extends JPanel implements PreferencesChangeListener {\n\tprotected static final ImageIcon CLOSE_ICON = new ImageIcon(TabbedPanel.class.getClassLoader().getResource(\"org/jd/gui/images/close.gif\"));\n    protected static final ImageIcon  CLOSE_ACTIVE_ICON = new ImageIcon(TabbedPanel.class.getClassLoader().getResource(\"org/jd/gui/images/close_active.gif\"));\n\n    protected static final String TAB_LAYOUT = \"UITabsPreferencesProvider.singleLineTabs\";\n\n    protected API api;\n    protected CardLayout cardLayout;\n    protected JTabbedPane tabbedPane;\n    protected Map<String, String> preferences;\n\n    public TabbedPanel(API api) {\n        this.api = api;\n\t\tcreate();\n\t}\n\n    protected void create() {\n\t\tsetLayout(cardLayout = new CardLayout());\n\t\tadd(\"panel\", new JPanel());\n\t\tadd(\"tabs\", tabbedPane = createTabPanel());\n\t}\n\n    protected JTabbedPane createTabPanel() {\n        JTabbedPane tabPanel = new JTabbedPane() {\n            @Override\n            public String getToolTipText(MouseEvent e) {\n                int index = indexAtLocation(e.getX(), e.getY());\n                if (index != -1) {\n                    return ((JComponent)getTabComponentAt(index)).getToolTipText();\n                }\n                return super.getToolTipText(e);\n            }\n        };\n        ToolTipManager.sharedInstance().registerComponent(tabPanel);\n        tabPanel.addMouseListener(new MouseAdapter() {\n            @Override public void mousePressed(MouseEvent e) { showPopupTabMenu(e); }\n            @Override public void mouseReleased(MouseEvent e) { showPopupTabMenu(e); }\n            protected void showPopupTabMenu(MouseEvent e) {\n                if (e.isPopupTrigger()) {\n                    int index = tabPanel.indexAtLocation(e.getX(), e.getY());\n                    if (index != -1) {\n                        new PopupTabMenu(tabPanel.getComponentAt(index)).show(e.getComponent(), e.getX(), e.getY());\n                    }\n                }\n            }\n        });\n        return tabPanel;\n\t}\n\n    protected static Color darker(Color c) {\n\t\treturn new Color(\n\t\t\tMath.max((int)(c.getRed()  *0.85), 0),\n\t\t\tMath.max((int)(c.getGreen()*0.85), 0),\n\t\t\tMath.max((int)(c.getBlue() *0.85), 0),\n\t\t\tc.getAlpha());\n\t}\n\n    public void addPage(String title, Icon icon, String tip, T page) {\n        // Add a new tab\n        JLabel tabCloseButton = new JLabel(CLOSE_ICON);\n        tabCloseButton.setToolTipText(\"Close this panel\");\n        tabCloseButton.addMouseListener(new MouseListener() {\n            @Override public void mousePressed(MouseEvent e) {}\n            @Override public void mouseReleased(MouseEvent e) {}\n            @Override public void mouseEntered(MouseEvent e) { ((JLabel)e.getSource()).setIcon(CLOSE_ACTIVE_ICON); }\n            @Override public void mouseExited(MouseEvent e) { ((JLabel)e.getSource()).setIcon(CLOSE_ICON); }\n            @Override public void mouseClicked(MouseEvent e) { removeComponent(page); }\n        });\n\n\t\tJPanel tab = new JPanel(new BorderLayout());\n        tab.setBorder(BorderFactory.createEmptyBorder(2, 0, 3, 0));\n\t\ttab.setOpaque(false);\n        tab.setToolTipText(tip);\n        tab.add(new JLabel(title, icon, JLabel.LEADING), BorderLayout.CENTER);\n\t\ttab.add(tabCloseButton, BorderLayout.EAST);\n        ToolTipManager.sharedInstance().unregisterComponent(tab);\n\n\t\tint index = tabbedPane.getTabCount();\n\t\ttabbedPane.addTab(title, page);\n        tabbedPane.setTabComponentAt(index, tab);\n        setSelectedIndex(index);\n\n        cardLayout.show(this, \"tabs\");\n\t}\n\n    protected void setSelectedIndex(int index) {\n        if (index != -1) {\n            if (tabbedPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) {\n                // Ensure that the new page is visible (bug with SCROLL_TAB_LAYOUT)\n                ChangeEvent event = new ChangeEvent(tabbedPane);\n                for (ChangeListener listener : tabbedPane.getChangeListeners()) {\n                    if (listener.getClass().getPackage().getName().startsWith(\"javax.\")) {\n                        listener.stateChanged(event);\n                    }\n                }\n            }\n\n            tabbedPane.setSelectedIndex(index);\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected T showPage(URI uri) {\n        String u1 = uri.getPath();\n        int i = tabbedPane.getTabCount();\n\n        while (i-- > 0) {\n            T page = (T)tabbedPane.getComponentAt(i);\n            String u2 = page.getUri().getPath();\n            if (u1.startsWith(u2)) {\n                tabbedPane.setSelectedIndex(i);\n                return page;\n            }\n        }\n\n        return null;\n    }\n\n    protected class PopupTabMenu extends JPopupMenu {\n        public PopupTabMenu(Component component) {\n            // Add default popup menu entries\n            JMenuItem menuItem = new JMenuItem(\"Close\", null);\n            menuItem.addActionListener(e -> removeComponent(component));\n            add(menuItem);\n\n            menuItem = new JMenuItem(\"Close Others\", null);\n            menuItem.addActionListener(e -> removeOtherComponents(component));\n            add(menuItem);\n\n            menuItem = new JMenuItem(\"Close All\", null);\n            menuItem.addActionListener(e -> removeAllComponents());\n            add(menuItem);\n\n            // Add \"Select Tab\" popup menu entry\n            if ((tabbedPane.getTabCount() > 1) && (PlatformService.getInstance().isMac() || \"true\".equals(preferences.get(TAB_LAYOUT)))) {\n                addSeparator();\n                JMenu menu = new JMenu(\"Select Tab\");\n                int count = tabbedPane.getTabCount();\n\n                for (int i = 0; i < count; i++) {\n                    JPanel tab = (JPanel) tabbedPane.getTabComponentAt(i);\n                    JLabel label = (JLabel) tab.getComponent(0);\n                    JMenuItem subMenuItem = new JMenuItem(label.getText(), label.getIcon());\n                    subMenuItem.addActionListener(new SubMenuItemActionListener(i));\n                    if (component == tabbedPane.getComponentAt(i)) {\n                        subMenuItem.setFont(subMenuItem.getFont().deriveFont(Font.BOLD));\n                    }\n                    menu.add(subMenuItem);\n                }\n\n                add(menu);\n            }\n\n            // Add SPI popup menu entries\n            if (component instanceof ContainerEntryGettable) {\n                Collection<Action> actions = api.getContextualActions(((ContainerEntryGettable)component).getEntry(), null);\n\n                if (actions != null) {\n                    addSeparator();\n\n                    for (Action action : actions) {\n                        if (action != null) {\n                            add(action);\n                        } else {\n                            addSeparator();\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    public JTabbedPane getTabbedPane() {\n        return tabbedPane;\n    }\n\n    protected class SubMenuItemActionListener implements ActionListener {\n        protected int index;\n\n        public SubMenuItemActionListener(int index) {\n            this.index = index;\n        }\n\n        @Override\n        public void actionPerformed(ActionEvent e) {\n            tabbedPane.setSelectedIndex(index);\n        }\n    }\n\n\n    // --- Popup menu actions --- //\n    public void removeComponent(Component component) {\n        tabbedPane.remove(component);\n        if (tabbedPane.getTabCount() == 0) {\n            cardLayout.show(this, \"panel\");\n        }\n    }\n\n    protected void removeOtherComponents(Component component) {\n        int i = tabbedPane.getTabCount();\n        while (i-- > 0) {\n            Component c = tabbedPane.getComponentAt(i);\n            if (c != component) {\n                tabbedPane.remove(i);\n            }\n        }\n        if (tabbedPane.getTabCount() == 0) {\n            cardLayout.show(this, \"panel\");\n        }\n    }\n\n    protected void removeAllComponents() {\n        tabbedPane.removeAll();\n        if (tabbedPane.getTabCount() == 0) {\n            cardLayout.show(this, \"panel\");\n        }\n    }\n\n    // --- PreferencesChangeListener --- //\n    @Override\n    public void preferencesChanged(Map<String, String> preferences) {\n        // Store preferences\n        this.preferences = preferences;\n        // Update layout\n        if (\"true\".equals(preferences.get(TAB_LAYOUT))) {\n            tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);\n        } else {\n            tabbedPane.setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT);\n        }\n        setSelectedIndex(tabbedPane.getSelectedIndex());\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/view/component/panel/TreeTabbedPanel.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view.component.panel;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.*;\nimport org.jd.gui.api.model.TreeNodeData;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.view.component.Tree;\nimport org.jd.gui.view.renderer.TreeNodeRenderer;\n\nimport javax.swing.*;\nimport javax.swing.event.TreeExpansionEvent;\nimport javax.swing.event.TreeExpansionListener;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport javax.swing.tree.DefaultTreeModel;\nimport javax.swing.tree.TreeNode;\nimport javax.swing.tree.TreePath;\nimport java.awt.*;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Enumeration;\nimport java.util.Map;\n\npublic class TreeTabbedPanel<T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> extends JPanel implements UriGettable, UriOpenable, PageChangeable, PageClosable, PreferencesChangeListener {\n    protected API api;\n    protected URI uri;\n    protected Tree tree;\n    protected TabbedPanel tabbedPanel;\n    protected ArrayList<PageChangeListener> pageChangedListeners = new ArrayList<>();\n    // Flags to prevent the event cascades\n    protected boolean updateTreeMenuEnabled = true;\n    protected boolean openUriEnabled = true;\n    protected boolean treeNodeChangedEnabled = true;\n\n    @SuppressWarnings(\"unchecked\")\n    public TreeTabbedPanel(API api, URI uri) {\n        this.api = api;\n        this.uri = uri;\n\n        tree = new Tree();\n        tree.setShowsRootHandles(true);\n        tree.setMinimumSize(new Dimension(150, 10));\n        tree.setExpandsSelectedPaths(true);\n        tree.setCellRenderer(new TreeNodeRenderer() {\n            @Override\n            public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {\n                // Always render the left tree with focus\n                return super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, true);\n            }\n        });\n        tree.addTreeSelectionListener(e -> treeNodeChanged((T)tree.getLastSelectedPathComponent()));\n        tree.addTreeExpansionListener(new TreeExpansionListener() {\n            @Override\n            public void treeExpanded(TreeExpansionEvent e) {\n                TreeNode node = (TreeNode)e.getPath().getLastPathComponent();\n                if (node instanceof TreeNodeExpandable) {\n                    TreeNodeExpandable tne = (TreeNodeExpandable)node;\n                    int oldHashCode = createHashCode(node.children());\n                    tne.populateTreeNode(api);\n                    int newHashCode = createHashCode(node.children());\n                    if (oldHashCode != newHashCode) {\n                        ((DefaultTreeModel)tree.getModel()).reload(node);\n                    }\n                }\n            }\n            @Override\n            public void treeCollapsed(TreeExpansionEvent e) {}\n        });\n        tree.addMouseListener(new MouseAdapter() {\n            @Override\n            public void mouseClicked(MouseEvent e) {\n                if (SwingUtilities.isRightMouseButton(e)) {\n                    TreePath path = tree.getPathForLocation(e.getX(), e.getY());\n\n                    if (path != null) {\n                        tree.setSelectionPath(path);\n\n                        T node = (T)path.getLastPathComponent();\n                        Collection<Action> actions = api.getContextualActions(node.getEntry(), node.getUri().getFragment());\n\n                        if (actions != null) {\n                            JPopupMenu popup = new JPopupMenu();\n                            for (Action action : actions) {\n                                if (action != null) {\n                                    popup.add(action);\n                                } else {\n                                    popup.addSeparator();\n                                }\n                            }\n                            popup.show(e.getComponent(), e.getX(), e.getY());\n                        }\n                    }\n                }\n            }\n        });\n\n        tabbedPanel = new TabbedPanel(api);\n        tabbedPanel.setMinimumSize(new Dimension(150, 10));\n        tabbedPanel.tabbedPane.addChangeListener(e -> pageChanged());\n\n        setLayout(new BorderLayout());\n\n        JSplitPane splitter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new JScrollPane(tree), tabbedPanel);\n        splitter.setResizeWeight(0.2);\n\n        add(splitter, BorderLayout.CENTER);\n    }\n\n    protected static int createHashCode(Enumeration enumeration) {\n        int hashCode = 1;\n\n        while (enumeration.hasMoreElements()) {\n            hashCode *= 31;\n\n            Object element = enumeration.nextElement();\n\n            if (element != null) {\n                hashCode += element.hashCode();\n            }\n        }\n\n        return hashCode;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected void treeNodeChanged(T node) {\n        if (treeNodeChangedEnabled && (node != null)) {\n            try {\n                // Disable tabbedPane.changeListener\n                updateTreeMenuEnabled = false;\n\n                // Search base tree node\n                URI uri = node.getUri();\n\n                if ((uri.getFragment() == null) && (uri.getQuery() == null)) {\n                    showPage(uri, uri, node);\n                } else {\n                    URI baseUri = new URI(uri.getScheme(), uri.getHost(), uri.getPath(), null);\n                    T baseNode = node;\n\n                    while ((baseNode != null) && !baseNode.getUri().equals(baseUri)) {\n                        baseNode = (T)baseNode.getParent();\n                    }\n\n                    if ((baseNode != null) && baseNode.getUri().equals(baseUri)) {\n                        showPage(uri, baseUri, baseNode);\n                    }\n                }\n            } catch (URISyntaxException e) {\n                assert ExceptionUtil.printStackTrace(e);\n            } finally {\n                // Enable tabbedPane.changeListener\n                updateTreeMenuEnabled = true;\n            }\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected <P extends JComponent & UriGettable> boolean showPage(URI uri, URI baseUri, DefaultMutableTreeNode baseNode) {\n        P page = (P)tabbedPanel.showPage(baseUri);\n\n        if ((page == null) && (baseNode instanceof PageCreator)) {\n            page = ((PageCreator)baseNode).createPage(api);\n            page.putClientProperty(\"node\", baseNode);\n\n            String path = baseUri.getPath();\n            String label = path.substring(path.lastIndexOf('/')+1);\n            Object data = baseNode.getUserObject();\n\n            if (data instanceof TreeNodeData) {\n                TreeNodeData tnd = (TreeNodeData)data;\n                tabbedPanel.addPage(label, tnd.getIcon(), tnd.getTip(), page);\n            } else {\n                tabbedPanel.addPage(label, null, null, page);\n            }\n        }\n\n        if (openUriEnabled && page instanceof UriOpenable) {\n            ((UriOpenable)page).openUri(uri);\n        }\n\n        return (page != null);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected <P extends JComponent & UriGettable> void pageChanged() {\n        try {\n            // Disable highlight\n            openUriEnabled = false;\n\n            P page = (P)tabbedPanel.tabbedPane.getSelectedComponent();\n\n            if (updateTreeMenuEnabled) {\n                // Synchronize tree\n                if (page != null) {\n                    T node = (T)page.getClientProperty(\"node\");\n                    // Select tree node\n                    TreePath treePath = new TreePath(node.getPath());\n                    tree.setSelectionPath(treePath);\n                    tree.scrollPathToVisible(treePath);\n                } else {\n                    tree.clearSelection();\n                }\n            }\n            // Fire page changed event\n            for (PageChangeListener listener : pageChangedListeners) {\n                listener.pageChanged(page);\n            }\n        } finally {\n            // Enable highlight\n            openUriEnabled = true;\n        }\n    }\n\n    // --- URIGetter --- //\n    @Override public URI getUri() { return uri; }\n\n    // --- URIOpener --- //\n    @Override\n    public boolean openUri(URI uri) {\n        try {\n            URI baseUri = new URI(uri.getScheme(), uri.getHost(), uri.getPath(), null);\n\n            if (this.uri.equals(baseUri)) {\n                return true;\n            } else {\n                DefaultMutableTreeNode node = searchTreeNode(baseUri, (DefaultMutableTreeNode) tree.getModel().getRoot());\n\n                if (showPage(uri, baseUri, node)) {\n                    DefaultMutableTreeNode childNode = searchTreeNode(uri, node);\n                    if (childNode != null) {\n                        node = childNode;\n                    }\n                }\n\n                if (node != null) {\n                    try {\n                        // Disable tree node changed listener\n                        treeNodeChangedEnabled = false;\n                        // Populate and expand node\n                        if (!(node instanceof PageCreator) && (node instanceof TreeNodeExpandable)) {\n                            ((TreeNodeExpandable) node).populateTreeNode(api);\n                            tree.expandPath(new TreePath(node.getPath()));\n                        }\n                        // Select tree node\n                        TreePath treePath = new TreePath(node.getPath());\n                        tree.setSelectionPath(treePath);\n                        tree.scrollPathToVisible(treePath);\n                    } finally {\n                        // Enable tree node changed listener\n                        treeNodeChangedEnabled = true;\n                    }\n                    return true;\n                }\n            }\n        } catch (URISyntaxException e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n\n        return false;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected DefaultMutableTreeNode searchTreeNode(URI uri, DefaultMutableTreeNode node) {\n        if (node instanceof TreeNodeExpandable) {\n            ((TreeNodeExpandable)node).populateTreeNode(api);\n        }\n\n        String u = uri.toString();\n        T child = null;\n        Enumeration enumeration = node.children();\n\n        while (enumeration.hasMoreElements()) {\n            T element = (T)enumeration.nextElement();\n            String childU = element.getUri().toString();\n\n            if (u.length() > childU.length()) {\n                if (u.startsWith(childU)) {\n                    char c = u.charAt(childU.length());\n                    if ((c == '/') || (c == '!')) {\n                        child = element;\n                        break;\n                    }\n                }\n            } else if (u.equals(childU)) {\n                child = element;\n                break;\n            }\n        }\n\n        if (child != null) {\n            if (u.equals(child.getUri().toString())) {\n                return child;\n            } else {\n                // Parent tree node found -> Recursive call\n                return searchTreeNode(uri, child);\n            }\n        } else {\n            // Not found\n            return null;\n        }\n    }\n\n    // --- PageChanger --- //\n    @Override\n    public void addPageChangeListener(PageChangeListener listener) {\n        pageChangedListeners.add(listener);\n    }\n\n    // --- PageCloser --- //\n    @Override\n    public boolean closePage() {\n        Component component = tabbedPanel.tabbedPane.getSelectedComponent();\n\n        if (component != null) {\n            tabbedPanel.removeComponent(component);\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    // --- PreferencesChangeListener --- //\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public void preferencesChanged(Map<String, String> preferences) {\n        tabbedPanel.preferencesChanged(preferences);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/view/renderer/OpenTypeListCellRenderer.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view.renderer;\n\nimport org.jd.gui.view.bean.OpenTypeListCellBean;\n\nimport javax.swing.*;\nimport java.awt.*;\n\npublic class OpenTypeListCellRenderer implements ListCellRenderer<OpenTypeListCellBean> {\n    protected Color textSelectionColor;\n    protected Color textNonSelectionColor;\n    protected Color infoSelectionColor;\n    protected Color infoNonSelectionColor;\n    protected Color backgroundSelectionColor;\n    protected Color backgroundNonSelectionColor;\n\n    protected JPanel panel;\n    protected JLabel label, info;\n\n    public OpenTypeListCellRenderer() {\n        textSelectionColor = UIManager.getColor(\"List.selectionForeground\");\n        textNonSelectionColor = UIManager.getColor(\"List.foreground\");\n        backgroundSelectionColor = UIManager.getColor(\"List.selectionBackground\");\n        backgroundNonSelectionColor = UIManager.getColor(\"List.background\");\n\n        infoSelectionColor = infoColor(textSelectionColor);\n        infoNonSelectionColor = infoColor(textNonSelectionColor);\n\n        panel = new JPanel(new BorderLayout());\n        panel.add(label = new JLabel(), BorderLayout.WEST);\n        panel.add(info = new JLabel(), BorderLayout.CENTER);\n    }\n\n    static protected Color infoColor(Color c) {\n        if (c.getRed() + c.getGreen() + c.getBlue() > (3*127)) {\n            return new Color(\n                    (int)((c.getRed()-127)  *0.7 + 127),\n                    (int)((c.getGreen()-127)*0.7 + 127),\n                    (int)((c.getBlue()-127) *0.7 + 127),\n                    c.getAlpha());\n        } else {\n            return new Color(\n                    (int)(127 - (127-c.getRed())  *0.7),\n                    (int)(127 - (127-c.getGreen())*0.7),\n                    (int)(127 - (127-c.getBlue()) *0.7),\n                    c.getAlpha());\n        }\n    }\n\n    @Override\n    public Component getListCellRendererComponent(JList<? extends OpenTypeListCellBean> list, OpenTypeListCellBean value, int index, boolean selected, boolean hasFocus) {\n        if (value != null) {\n            // Display first level item\n            label.setText(value.label);\n            label.setIcon(value.icon);\n\n            info.setText((value.packag != null) ? \" - \"+value.packag : \"\");\n\n            if (selected) {\n                label.setForeground(textSelectionColor);\n                info.setForeground(infoSelectionColor);\n                panel.setBackground(backgroundSelectionColor);\n            } else {\n                label.setForeground(textNonSelectionColor);\n                info.setForeground(infoNonSelectionColor);\n                panel.setBackground(backgroundNonSelectionColor);\n            }\n        } else {\n            label.setText(\" ...\");\n            label.setIcon(null);\n            info.setText(\"\");\n            label.setForeground(textNonSelectionColor);\n            panel.setBackground(backgroundNonSelectionColor);\n        }\n\n        return panel;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/org/jd/gui/view/renderer/TreeNodeRenderer.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view.renderer;\n\nimport org.jd.gui.api.model.TreeNodeData;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport javax.swing.tree.TreeCellRenderer;\nimport java.awt.*;\n\npublic class TreeNodeRenderer implements TreeCellRenderer {\n    protected Color textSelectionColor;\n    protected Color backgroundSelectionColor;\n    protected Color textNonSelectionColor;\n    protected Color backgroundNonSelectionColor;\n    protected Color textDisabledColor;\n    protected Color backgroundDisabledColor;\n\n    protected JPanel panel;\n    protected JLabel icon, label;\n\n    public TreeNodeRenderer() {\n        panel = new JPanel(new BorderLayout());\n        panel.add(icon = new JLabel(), BorderLayout.WEST);\n        panel.add(label = new JLabel(), BorderLayout.CENTER);\n        panel.setOpaque(false);\n\n        textSelectionColor = UIManager.getColor(\"Tree.selectionForeground\");\n        backgroundSelectionColor = UIManager.getColor(\"Tree.selectionBackground\");\n        textNonSelectionColor = UIManager.getColor(\"Tree.textForeground\");\n        backgroundNonSelectionColor = UIManager.getColor(\"Tree.textBackground\");\n        textDisabledColor = UIManager.getColor(\"Tree.disabledText\");\n        backgroundDisabledColor = UIManager.getColor(\"Tree.disabled\");\n        Insets margins = UIManager.getInsets(\"Tree.rendererMargins\");\n\n        icon.setForeground(textNonSelectionColor);\n        icon.setOpaque(false);\n        icon.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 2));\n\n        label.setOpaque(false);\n\n        if (margins != null) {\n            label.setBorder(BorderFactory.createEmptyBorder(margins.top, margins.left, margins.bottom, margins.right));\n        } else {\n            label.setBorder(BorderFactory.createEmptyBorder(0, 4, 0, 4));\n        }\n    }\n\n    @Override\n    public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {\n        Object data = ((DefaultMutableTreeNode)value).getUserObject();\n\n        if (data instanceof TreeNodeData) {\n            TreeNodeData tnd = (TreeNodeData)data;\n            icon.setIcon(expanded && (tnd.getOpenIcon() != null) ? tnd.getOpenIcon() : tnd.getIcon());\n            label.setText(tnd.getLabel());\n        } else {\n            icon.setIcon(null);\n            label.setText(\"\" + data);\n        }\n\n        if (selected) {\n            if (hasFocus) {\n                label.setForeground(textSelectionColor);\n                label.setBackground(backgroundSelectionColor);\n            } else {\n                label.setForeground(textDisabledColor);\n                label.setBackground(backgroundDisabledColor);\n            }\n            label.setOpaque(true);\n        } else {\n            label.setForeground(textNonSelectionColor);\n            label.setOpaque(false);\n        }\n\n        return panel;\n    }\n}\n"
  },
  {
    "path": "app/src/main/resources/META-INF/services/org.jd.gui.spi.PanelFactory",
    "content": "org.jd.gui.service.mainpanel.ContainerPanelFactoryProvider\n"
  },
  {
    "path": "app/src/main/resources/META-INF/services/org.jd.gui.spi.PreferencesPanel",
    "content": "org.jd.gui.service.preferencespanel.UISingleInstancePreferencesProvider\norg.jd.gui.service.preferencespanel.UITabsPreferencesProvider\n"
  },
  {
    "path": "build.gradle",
    "content": "buildscript {\n    repositories {\n        jcenter()\n    }\n    dependencies {\n        classpath 'com.netflix.nebula:gradle-ospackage-plugin:5.3.0'    // RPM & DEB support\n        classpath 'edu.sc.seis.gradle:launch4j:2.4.4'\n        classpath 'net.sf.proguard:proguard-gradle:6.1.0'\n    }\n}\n\napply plugin: 'java'\napply plugin: 'distribution'\napply plugin: 'edu.sc.seis.launch4j'\napply plugin: 'nebula.ospackage'\n\n// Common configuration //\nrootProject.version='1.6.6'\nrootProject.ext.set('jdCoreVersion', '1.1.3')\ntargetCompatibility = '1.8'\n\nallprojects {\n    apply plugin: 'eclipse'\n    apply plugin: 'idea'\n\n    tasks.withType(JavaCompile) {\n        sourceCompatibility = targetCompatibility = '1.8'\n        options.compilerArgs << '-Xlint:deprecation'\n        options.compilerArgs << '-Xlint:unchecked'\n        options.encoding = 'UTF-8'\n    }\n\n    repositories {\n        jcenter()\n    }\n\n    configurations {\n        provided\n        compile.extendsFrom provided\n    }\n}\n\n// 'cleanIdea' task extension //\ncleanIdea.doFirst {\n    delete project.name + '.iws'\n    delete 'out'\n    followSymlinks = true\n}\n\n// All in one JAR file //\nsubprojects.each { subproject ->\n    evaluationDependsOn(subproject.path)\n}\n\njar {\n    dependsOn subprojects.tasks['jar']\n\n    // Add SPI directory\n    def tmpSpiDir = file('build/tmp/spi')\n    from tmpSpiDir\n    // Add dependencies\n    def deps = []\n    subprojects.each { subproject ->\n        from subproject.sourceSets.main.output.classesDirs\n        from subproject.sourceSets.main.output.resourcesDir\n        deps += subproject.configurations.runtime - subproject.configurations.provided\n    }\n    subprojects.each { subproject ->\n        deps -= subproject.jar.archivePath\n    }\n    deps = deps.unique().collect { it.isDirectory() ? it : zipTree(it) }\n    from deps\n\n    manifest {\n        attributes 'Main-Class': 'org.jd.gui.App',\n                'SplashScreen-Image': 'org/jd/gui/images/jd_icon_128.png',\n                'JD-GUI-Version': project.version,\n                'JD-Core-Version': project.jdCoreVersion\n    }\n    exclude 'META-INF/licenses/**', 'META-INF/maven/**', 'META-INF/INDEX.LIST'\n    exclude '**/ErrorStrip_*.properties', '**/RSyntaxTextArea_*.properties', '**/RTextArea_*.properties'\n    exclude '**/FocusableTip_*.properties', '**/RSyntaxTextArea_License.txt'\n    duplicatesStrategy DuplicatesStrategy.EXCLUDE\n    doFirst {\n        // Create SPI directory\n        tmpSpiDir.deleteDir()\n        def tmpSpiServicesDir = file(tmpSpiDir.path + '/META-INF/services')\n        tmpSpiServicesDir.mkdirs()\n        // Copy and merge SPI config files\n        subprojects.each { subproject ->\n            def servicesDir = file(subproject.sourceSets.main.output.resourcesDir.path + '/META-INF/services')\n            if (servicesDir.exists()) {\n                servicesDir.eachFile { serviceFile ->\n                    def target = file(tmpSpiServicesDir.path + '/' + serviceFile.name)\n                    target << serviceFile.text\n                }\n            }\n        }\n    }\n}\n\n// Minify JAR file //\ntask proguard(type: proguard.gradle.ProGuardTask, dependsOn: 'jar') {\n    configuration 'src/proguard/resources/proguard.config.txt'\n    injars jar.archivePath\n    outjars 'build/libs/' + project.name + '-' + project.version + '-min.jar'\n    libraryjars System.getProperty('java.home') + '/lib/rt.jar'\n    libraryjars System.getProperty('java.home') + '/jmods/'\n}\n\n// Java executable wrapper for Windows //\nlaunch4j {\n    createExe.dependsOn 'proguard'\n\n    version = textVersion = project.version\n    fileDescription = productName = 'JD-GUI'\n    errTitle 'JD-GUI Windows Wrapper'\n    copyright 'JD-GUI (C) 2008-2019 Emmanuel Dupuy'\n    icon projectDir.path + '/src/launch4j/resources/images/jd-gui.ico'\n    jar projectDir.path + '/' + proguard.outJarFiles[0]\n    bundledJrePath = '%JAVA_HOME%'\n}\n\n// Packages for Linux //\nospackage {\n    buildDeb.dependsOn 'proguard'\n    buildRpm.dependsOn 'proguard'\n\n    license = file('LICENSE')\n    maintainer 'Emmanuel Dupuy <emmanue1@users.noreply.github.com>'\n    os LINUX\n    packageDescription 'JD-GUI, a standalone graphical utility that displays Java sources from CLASS files'\n    packageGroup 'java'\n    packageName project.name\n    release '0'\n    summary 'A Java Decompiler'\n    url 'https://github.com/java-decompiler/jd-gui'\n\n    into '/opt/' + project.name\n    from (proguard.outJarFiles[0]) {\n        fileMode 0755\n    }\n    from ('src/linux/resources/') {\n        fileMode 0755\n    }\n    from 'LICENSE', 'NOTICE', 'README.md'\n\n    postInstall 'cd /opt/' + project.name + '; ln -s ./' + file(proguard.outJarFiles[0]).name + ' ./jd-gui.jar; xdg-icon-resource install --size 128 --novendor ./jd_icon_128.png jd-gui; xdg-desktop-menu install ./*.desktop'\n    preUninstall 'cd /opt/' + project.name + '; rm -f ./jd-gui.jar; rm -fr ./ext; xdg-desktop-menu uninstall ./*.desktop'\n}\n\n// Distributions for OSX and Windows //\ndistributions {\n    osx.contents {\n        into('JD-GUI.app/Contents') {\n            from('src/osx/resources') {\n                include 'Info.plist'\n                expand VERSION: project.version,\n                       JAR: file(proguard.outJarFiles[0]).name\n            }\n        }\n        into('JD-GUI.app/Contents/MacOS') {\n            from('src/osx/resources') {\n                include 'universalJavaApplicationStub.sh'\n                fileMode 0755\n            }\n        }\n        into('JD-GUI.app/Contents/Resources/Java') {\n            from proguard.outJarFiles[0]\n        }\n        from 'LICENSE', 'NOTICE', 'README.md'\n    }\n    windows.contents {\n        from 'build/launch4j/jd-gui.exe'\n        from 'LICENSE', 'NOTICE', 'README.md'\n    }\n\n    installWindowsDist.dependsOn createExe\n    windowsDistTar.dependsOn createExe\n    windowsDistZip.dependsOn createExe\n\n    installOsxDist.dependsOn 'proguard'\n    osxDistTar.dependsOn 'proguard'\n    osxDistZip.dependsOn 'proguard'\n}\n\nbuild.finalizedBy buildDeb\nbuild.finalizedBy buildRpm\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Sat Mar 02 11:11:32 CET 2019\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-5.2.1-bin.zip\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# For Cygwin, ensure paths are in UNIX format before anything is touched.\nif $cygwin ; then\n    [ -n \"$JAVA_HOME\" ] && JAVA_HOME=`cygpath --unix \"$JAVA_HOME\"`\nfi\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >&-\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >&-\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windowz variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\ngoto execute\r\n\r\n:4NT_args\r\n@rem Get arguments from the 4NT Shell from JP Software\r\nset CMD_LINE_ARGS=%$\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "services/build.gradle",
    "content": "apply plugin: 'java'\n\ndependencies {\n    compile 'com.fifesoft:rsyntaxtextarea:3.0.4'\n    compile 'org.ow2.asm:asm:7.1'\n    compile 'org.jd:jd-core:' + parent.jdCoreVersion\n    compile project(':api')\n    testCompile 'junit:junit:4.12'\n}\n\nversion = parent.version\n\n// ANTLR //\next.antlr4 = [\n        antlrSource: 'src/main/antlr',\n        destinationDir: 'src-generated/antlr/java',\n        grammarPackage: 'org.jd.gui.util.parser.antlr'\n]\n\nconfigurations {\n    antlr4 {\n        description = \"ANTLR4\"\n    }\n}\n\ndependencies {\n    compile 'org.antlr:antlr4-runtime:4.5'\n    antlr4 'org.antlr:antlr4:4.5'\n}\n\ntask antlr4OutputDir() {\n    mkdir antlr4.destinationDir\n}\n\ntask antlr4GenerateGrammarSource(dependsOn: antlr4OutputDir, type: JavaExec) {\n    description = 'Generates Java sources from ANTLR4 grammars.'\n\n    inputs.dir file(antlr4.antlrSource)\n    outputs.dir file(antlr4.destinationDir)\n\n    def grammars = fileTree(antlr4.antlrSource).include('**/*.g4')\n    def pkg = antlr4.grammarPackage.replaceAll(\"\\\\.\", \"/\")\n\n    main = 'org.antlr.v4.Tool'\n    classpath = configurations.antlr4\n    args = ['-o', \"${antlr4.destinationDir}/${pkg}\", '-package', antlr4.grammarPackage, grammars.files].flatten()\n}\n\ncompileJava {\n    dependsOn antlr4GenerateGrammarSource\n    source antlr4.destinationDir\n}\n\nclean {\n    delete 'src-generated'\n}\n\nidea.module {\n    sourceDirs += file(antlr4.destinationDir)\n}\nideaModule.dependsOn antlr4GenerateGrammarSource\n\neclipse.classpath.file.withXml { xml ->\n    def node = xml.asNode()\n    node.appendNode( 'classpathentry', [ kind: 'src', path: antlr4.destinationDir])\n}\neclipseClasspath.dependsOn antlr4GenerateGrammarSource\n"
  },
  {
    "path": "services/src/main/antlr/Java.g4",
    "content": "/*\n [The \"BSD licence\"]\n Copyright (c) 2013 Terence Parr, Sam Harwell\n All rights reserved.\n\n Redistribution and use in source and binary forms, with or without\n modification, are permitted provided that the following conditions\n are met:\n 1. Redistributions of source code must retain the above copyright\n    notice, this list of conditions and the following disclaimer.\n 2. Redistributions in binary form must reproduce the above copyright\n    notice, this list of conditions and the following disclaimer in the\n    documentation and/or other materials provided with the distribution.\n 3. The name of the author may not be used to endorse or promote products\n    derived from this software without specific prior written permission.\n\n THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\n THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n*/\n\n/** A Java 1.7 grammar for ANTLR v4 derived from ANTLR v3 Java grammar.\n *  Uses ANTLR v4's left-recursive expression notation.\n *  It parses ECJ, Netbeans, JDK etc...\n *\n *  Sam Harwell cleaned this up significantly and updated to 1.7!\n *\n *  You can test with\n *\n *  $ antlr4 Java.g4\n *  $ javac *.java\n *  $ grun Java compilationUnit *.java\n */\ngrammar Java;\n\n// starting point for parsing a java file\ncompilationUnit\n    :   packageDeclaration? importDeclaration* typeDeclaration* EOF\n    ;\n\npackageDeclaration\n    :   annotation* 'package' qualifiedName ';'\n    ;\n\nimportDeclaration\n    :   'import' 'static'? qualifiedName ('.' '*')? ';'\n    ;\n\ntypeDeclaration\n    :   classOrInterfaceModifier* classDeclaration\n    |   classOrInterfaceModifier* enumDeclaration\n    |   classOrInterfaceModifier* interfaceDeclaration\n    |   classOrInterfaceModifier* annotationTypeDeclaration\n    |   ';'\n    ;\n\nmodifier\n    :   classOrInterfaceModifier\n    |   (   'native'\n        |   'synchronized'\n        |   'transient'\n        |   'volatile'\n        )\n    ;\n\nclassOrInterfaceModifier\n    :   annotation       // class or interface\n    |   (   'public'     // class or interface\n        |   'protected'  // class or interface\n        |   'private'    // class or interface\n        |   'static'     // class or interface\n        |   'abstract'   // class or interface\n        |   'final'      // class only -- does not apply to interfaces\n        |   'strictfp'   // class or interface\n        )\n    ;\n\nvariableModifier\n    :   'final'\n    |   annotation\n    ;\n\nclassDeclaration\n    :   'class' Identifier typeParameters?\n        ('extends' type)?\n        ('implements' typeList)?\n        classBody\n    ;\n\ntypeParameters\n    :   '<' typeParameter (',' typeParameter)* '>'\n    ;\n\ntypeParameter\n    :   Identifier ('extends' typeBound)?\n    ;\n\ntypeBound\n    :   type ('&' type)*\n    ;\n\nenumDeclaration\n    :   ENUM Identifier ('implements' typeList)?\n        '{' enumConstants? ','? enumBodyDeclarations? '}'\n    ;\n\nenumConstants\n    :   enumConstant (',' enumConstant)*\n    ;\n\nenumConstant\n    :   annotation* Identifier arguments? classBody?\n    ;\n\nenumBodyDeclarations\n    :   ';' classBodyDeclaration*\n    ;\n\ninterfaceDeclaration\n    :   'interface' Identifier typeParameters? ('extends' typeList)? interfaceBody\n    ;\n\ntypeList\n    :   type (',' type)*\n    ;\n\nclassBody\n    :   '{' classBodyDeclaration* '}'\n    ;\n\ninterfaceBody\n    :   '{' interfaceBodyDeclaration* '}'\n    ;\n\nclassBodyDeclaration\n    :   ';'\n    |   'static'? block\n    |   modifier* memberDeclaration\n    ;\n\nmemberDeclaration\n    :   methodDeclaration\n    |   genericMethodDeclaration\n    |   fieldDeclaration\n    |   constructorDeclaration\n    |   genericConstructorDeclaration\n    |   interfaceDeclaration\n    |   annotationTypeDeclaration\n    |   classDeclaration\n    |   enumDeclaration\n    ;\n\n/* We use rule this even for void methods which cannot have [] after parameters.\n   This simplifies grammar and we can consider void to be a type, which\n   renders the [] matching as a context-sensitive issue or a semantic check\n   for invalid return type after parsing.\n */\nmethodDeclaration\n    :   (type|'void') Identifier formalParameters ('[' ']')*\n        ('throws' qualifiedNameList)?\n        (   methodBody\n        |   ';'\n        )\n    ;\n\ngenericMethodDeclaration\n    :   typeParameters methodDeclaration\n    ;\n\nconstructorDeclaration\n    :   Identifier formalParameters ('throws' qualifiedNameList)?\n        constructorBody\n    ;\n\ngenericConstructorDeclaration\n    :   typeParameters constructorDeclaration\n    ;\n\nfieldDeclaration\n    :   type variableDeclarators ';'\n    ;\n\ninterfaceBodyDeclaration\n    :   modifier* interfaceMemberDeclaration\n    |   ';'\n    ;\n\ninterfaceMemberDeclaration\n    :   constDeclaration\n    |   interfaceMethodDeclaration\n    |   genericInterfaceMethodDeclaration\n    |   interfaceDeclaration\n    |   annotationTypeDeclaration\n    |   classDeclaration\n    |   enumDeclaration\n    ;\n\nconstDeclaration\n    :   type constantDeclarator (',' constantDeclarator)* ';'\n    ;\n\nconstantDeclarator\n    :   Identifier ('[' ']')* '=' variableInitializer\n    ;\n\n// see matching of [] comment in methodDeclaratorRest\ninterfaceMethodDeclaration\n    :   (type|'void') Identifier formalParameters ('[' ']')*\n        ('throws' qualifiedNameList)?\n        ';'\n    ;\n\ngenericInterfaceMethodDeclaration\n    :   typeParameters interfaceMethodDeclaration\n    ;\n\nvariableDeclarators\n    :   variableDeclarator (',' variableDeclarator)*\n    ;\n\nvariableDeclarator\n    :   variableDeclaratorId ('=' variableInitializer)?\n    ;\n\nvariableDeclaratorId\n    :   Identifier ('[' ']')*\n    ;\n\nvariableInitializer\n    :   arrayInitializer\n    |   expression\n    ;\n\narrayInitializer\n    :   '{' (variableInitializer (',' variableInitializer)* (',')? )? '}'\n    ;\n\nenumConstantName\n    :   Identifier\n    ;\n\ntype\n    :   classOrInterfaceType ('[' ']')*\n    |   primitiveType ('[' ']')*\n    ;\n\nclassOrInterfaceType\n    :   Identifier typeArguments? ('.' Identifier typeArguments? )*\n    ;\n\nprimitiveType\n    :   'boolean'\n    |   'char'\n    |   'byte'\n    |   'short'\n    |   'int'\n    |   'long'\n    |   'float'\n    |   'double'\n    ;\n\ntypeArguments\n    :   '<' typeArgument (',' typeArgument)* '>'\n    ;\n\ntypeArgument\n    :   type\n    |   '?' (('extends' | 'super') type)?\n    ;\n\nqualifiedNameList\n    :   qualifiedName (',' qualifiedName)*\n    ;\n\nformalParameters\n    :   '(' formalParameterList? ')'\n    ;\n\nformalParameterList\n    :   formalParameter (',' formalParameter)* (',' lastFormalParameter)?\n    |   lastFormalParameter\n    ;\n\nformalParameter\n    :   variableModifier* type variableDeclaratorId\n    ;\n\nlastFormalParameter\n    :   variableModifier* type '...' variableDeclaratorId\n    ;\n\nmethodBody\n    :   block\n    ;\n\nconstructorBody\n    :   block\n    ;\n\nqualifiedName\n    :   Identifier ('.' Identifier)*\n    ;\n\nliteral\n    :   IntegerLiteral\n    |   FloatingPointLiteral\n    |   CharacterLiteral\n    |   StringLiteral\n    |   BooleanLiteral\n    |   'null'\n    ;\n\n// ANNOTATIONS\n\nannotation\n    :   '@' annotationName ( '(' ( elementValuePairs | elementValue )? ')' )?\n    ;\n\nannotationName : qualifiedName ;\n\nelementValuePairs\n    :   elementValuePair (',' elementValuePair)*\n    ;\n\nelementValuePair\n    :   Identifier '=' elementValue\n    ;\n\nelementValue\n    :   expression\n    |   annotation\n    |   elementValueArrayInitializer\n    ;\n\nelementValueArrayInitializer\n    :   '{' (elementValue (',' elementValue)*)? (',')? '}'\n    ;\n\nannotationTypeDeclaration\n    :   '@' 'interface' Identifier annotationTypeBody\n    ;\n\nannotationTypeBody\n    :   '{' (annotationTypeElementDeclaration)* '}'\n    ;\n\nannotationTypeElementDeclaration\n    :   modifier* annotationTypeElementRest\n    |   ';' // this is not allowed by the grammar, but apparently allowed by the actual compiler\n    ;\n\nannotationTypeElementRest\n    :   type annotationMethodOrConstantRest ';'\n    |   classDeclaration ';'?\n    |   interfaceDeclaration ';'?\n    |   enumDeclaration ';'?\n    |   annotationTypeDeclaration ';'?\n    ;\n\nannotationMethodOrConstantRest\n    :   annotationMethodRest\n    |   annotationConstantRest\n    ;\n\nannotationMethodRest\n    :   Identifier '(' ')' defaultValue?\n    ;\n\nannotationConstantRest\n    :   variableDeclarators\n    ;\n\ndefaultValue\n    :   'default' elementValue\n    ;\n\n// STATEMENTS / BLOCKS\n\nblock\n    :   '{' blockStatement* '}'\n    ;\n\nblockStatement\n    :   localVariableDeclarationStatement\n    |   statement\n    |   typeDeclaration\n    ;\n\nlocalVariableDeclarationStatement\n    :    localVariableDeclaration ';'\n    ;\n\nlocalVariableDeclaration\n    :   variableModifier* type variableDeclarators\n    ;\n\nstatement\n    :   block\n    |   ASSERT expression (':' expression)? ';'\n    |   'if' parExpression statement ('else' statement)?\n    |   'for' '(' forControl ')' statement\n    |   'while' parExpression statement\n    |   'do' statement 'while' parExpression ';'\n    |   'try' block (catchClause+ finallyBlock? | finallyBlock)\n    |   'try' resourceSpecification block catchClause* finallyBlock?\n    |   'switch' parExpression '{' switchBlockStatementGroup* switchLabel* '}'\n    |   'synchronized' parExpression block\n    |   'return' expression? ';'\n    |   'throw' expression ';'\n    |   'break' Identifier? ';'\n    |   'continue' Identifier? ';'\n    |   ';'\n    |   statementExpression ';'\n    |   Identifier ':' statement\n    ;\n\ncatchClause\n    :   'catch' '(' variableModifier* catchType Identifier ')' block\n    ;\n\ncatchType\n    :   qualifiedName ('|' qualifiedName)*\n    ;\n\nfinallyBlock\n    :   'finally' block\n    ;\n\nresourceSpecification\n    :   '(' resources ';'? ')'\n    ;\n\nresources\n    :   resource (';' resource)*\n    ;\n\nresource\n    :   variableModifier* classOrInterfaceType variableDeclaratorId '=' expression\n    ;\n\n/** Matches cases then statements, both of which are mandatory.\n *  To handle empty cases at the end, we add switchLabel* to statement.\n */\nswitchBlockStatementGroup\n    :   switchLabel+ blockStatement+\n    ;\n\nswitchLabel\n    :   'case' constantExpression ':'\n    |   'case' enumConstantName ':'\n    |   'default' ':'\n    ;\n\nforControl\n    :   enhancedForControl\n    |   forInit? ';' expression? ';' forUpdate?\n    ;\n\nforInit\n    :   localVariableDeclaration\n    |   expressionList\n    ;\n\nenhancedForControl\n    :   variableModifier* type variableDeclaratorId ':' expression\n    ;\n\nforUpdate\n    :   expressionList\n    ;\n\n// EXPRESSIONS\n\nparExpression\n    :   '(' expression ')'\n    ;\n\nexpressionList\n    :   expression (',' expression)*\n    ;\n\nstatementExpression\n    :   expression\n    ;\n\nconstantExpression\n    :   expression\n    ;\n\nexpression\n    :   primary\n    |   expression '.' Identifier\n    |   expression '.' 'this'\n    |   expression '.' 'new' nonWildcardTypeArguments? innerCreator\n    |   expression '.' 'super' superSuffix\n    |   expression '.' explicitGenericInvocation\n    |   expression '[' expression ']'\n    |   expression '(' expressionList? ')'\n    |   'new' creator\n    |   '(' type ')' expression\n    |   expression ('++' | '--')\n    |   ('+'|'-'|'++'|'--') expression\n    |   ('~'|'!') expression\n    |   expression ('*'|'/'|'%') expression\n    |   expression ('+'|'-') expression\n    |   expression ('<' '<' | '>' '>' '>' | '>' '>') expression\n    |   expression ('<=' | '>=' | '>' | '<') expression\n    |   expression 'instanceof' type\n    |   expression ('==' | '!=') expression\n    |   expression '&' expression\n    |   expression '^' expression\n    |   expression '|' expression\n    |   expression '&&' expression\n    |   expression '||' expression\n    |   expression '?' expression ':' expression\n    |   <assoc=right> expression\n        (   '='\n        |   '+='\n        |   '-='\n        |   '*='\n        |   '/='\n        |   '&='\n        |   '|='\n        |   '^='\n        |   '>>='\n        |   '>>>='\n        |   '<<='\n        |   '%='\n        )\n        expression\n    ;\n\nprimary\n    :   '(' expression ')'\n    |   'this'\n    |   'super'\n    |   literal\n    |   Identifier\n    |   type '.' 'class'\n    |   'void' '.' 'class'\n    |   nonWildcardTypeArguments (explicitGenericInvocationSuffix | 'this' arguments)\n    ;\n\ncreator\n    :   nonWildcardTypeArguments createdName classCreatorRest\n    |   createdName (arrayCreatorRest | classCreatorRest)\n    ;\n\ncreatedName\n    :   Identifier typeArgumentsOrDiamond? ('.' Identifier typeArgumentsOrDiamond?)*\n    |   primitiveType\n    ;\n\ninnerCreator\n    :   Identifier nonWildcardTypeArgumentsOrDiamond? classCreatorRest\n    ;\n\narrayCreatorRest\n    :   '['\n        (   ']' ('[' ']')* arrayInitializer\n        |   expression ']' ('[' expression ']')* ('[' ']')*\n        )\n    ;\n\nclassCreatorRest\n    :   arguments classBody?\n    ;\n\nexplicitGenericInvocation\n    :   nonWildcardTypeArguments explicitGenericInvocationSuffix\n    ;\n\nnonWildcardTypeArguments\n    :   '<' typeList '>'\n    ;\n\ntypeArgumentsOrDiamond\n    :   '<' '>'\n    |   typeArguments\n    ;\n\nnonWildcardTypeArgumentsOrDiamond\n    :   '<' '>'\n    |   nonWildcardTypeArguments\n    ;\n\nsuperSuffix\n    :   arguments\n    |   '.' Identifier arguments?\n    ;\n\nexplicitGenericInvocationSuffix\n    :   'super' superSuffix\n    |   Identifier arguments\n    ;\n\narguments\n    :   '(' expressionList? ')'\n    ;\n\n// LEXER\n\n// §3.9 Keywords\n\nABSTRACT      : 'abstract';\nASSERT        : 'assert';\nBOOLEAN       : 'boolean';\nBREAK         : 'break';\nBYTE          : 'byte';\nCASE          : 'case';\nCATCH         : 'catch';\nCHAR          : 'char';\nCLASS         : 'class';\nCONST         : 'const';\nCONTINUE      : 'continue';\nDEFAULT       : 'default';\nDO            : 'do';\nDOUBLE        : 'double';\nELSE          : 'else';\nENUM          : 'enum';\nEXTENDS       : 'extends';\nFINAL         : 'final';\nFINALLY       : 'finally';\nFLOAT         : 'float';\nFOR           : 'for';\nIF            : 'if';\nGOTO          : 'goto';\nIMPLEMENTS    : 'implements';\nIMPORT        : 'import';\nINSTANCEOF    : 'instanceof';\nINT           : 'int';\nINTERFACE     : 'interface';\nLONG          : 'long';\nNATIVE        : 'native';\nNEW           : 'new';\nPACKAGE       : 'package';\nPRIVATE       : 'private';\nPROTECTED     : 'protected';\nPUBLIC        : 'public';\nRETURN        : 'return';\nSHORT         : 'short';\nSTATIC        : 'static';\nSTRICTFP      : 'strictfp';\nSUPER         : 'super';\nSWITCH        : 'switch';\nSYNCHRONIZED  : 'synchronized';\nTHIS          : 'this';\nTHROW         : 'throw';\nTHROWS        : 'throws';\nTRANSIENT     : 'transient';\nTRY           : 'try';\nVOID          : 'void';\nVOLATILE      : 'volatile';\nWHILE         : 'while';\n\n// §3.10.1 Integer Literals\n\nIntegerLiteral\n    :   DecimalIntegerLiteral\n    |   HexIntegerLiteral\n    |   OctalIntegerLiteral\n    |   BinaryIntegerLiteral\n    ;\n\nfragment\nDecimalIntegerLiteral\n    :   DecimalNumeral IntegerTypeSuffix?\n    ;\n\nfragment\nHexIntegerLiteral\n    :   HexNumeral IntegerTypeSuffix?\n    ;\n\nfragment\nOctalIntegerLiteral\n    :   OctalNumeral IntegerTypeSuffix?\n    ;\n\nfragment\nBinaryIntegerLiteral\n    :   BinaryNumeral IntegerTypeSuffix?\n    ;\n\nfragment\nIntegerTypeSuffix\n    :   [lL]\n    ;\n\nfragment\nDecimalNumeral\n    :   '0'\n    |   NonZeroDigit (Digits? | Underscores Digits)\n    ;\n\nfragment\nDigits\n    :   Digit (DigitOrUnderscore* Digit)?\n    ;\n\nfragment\nDigit\n    :   '0'\n    |   NonZeroDigit\n    ;\n\nfragment\nNonZeroDigit\n    :   [1-9]\n    ;\n\nfragment\nDigitOrUnderscore\n    :   Digit\n    |   '_'\n    ;\n\nfragment\nUnderscores\n    :   '_'+\n    ;\n\nfragment\nHexNumeral\n    :   '0' [xX] HexDigits\n    ;\n\nfragment\nHexDigits\n    :   HexDigit (HexDigitOrUnderscore* HexDigit)?\n    ;\n\nfragment\nHexDigit\n    :   [0-9a-fA-F]\n    ;\n\nfragment\nHexDigitOrUnderscore\n    :   HexDigit\n    |   '_'\n    ;\n\nfragment\nOctalNumeral\n    :   '0' Underscores? OctalDigits\n    ;\n\nfragment\nOctalDigits\n    :   OctalDigit (OctalDigitOrUnderscore* OctalDigit)?\n    ;\n\nfragment\nOctalDigit\n    :   [0-7]\n    ;\n\nfragment\nOctalDigitOrUnderscore\n    :   OctalDigit\n    |   '_'\n    ;\n\nfragment\nBinaryNumeral\n    :   '0' [bB] BinaryDigits\n    ;\n\nfragment\nBinaryDigits\n    :   BinaryDigit (BinaryDigitOrUnderscore* BinaryDigit)?\n    ;\n\nfragment\nBinaryDigit\n    :   [01]\n    ;\n\nfragment\nBinaryDigitOrUnderscore\n    :   BinaryDigit\n    |   '_'\n    ;\n\n// §3.10.2 Floating-Point Literals\n\nFloatingPointLiteral\n    :   DecimalFloatingPointLiteral\n    |   HexadecimalFloatingPointLiteral\n    ;\n\nfragment\nDecimalFloatingPointLiteral\n    :   Digits '.' Digits? ExponentPart? FloatTypeSuffix?\n    |   '.' Digits ExponentPart? FloatTypeSuffix?\n    |   Digits ExponentPart FloatTypeSuffix?\n    |   Digits FloatTypeSuffix\n    ;\n\nfragment\nExponentPart\n    :   ExponentIndicator SignedInteger\n    ;\n\nfragment\nExponentIndicator\n    :   [eE]\n    ;\n\nfragment\nSignedInteger\n    :   Sign? Digits\n    ;\n\nfragment\nSign\n    :   [+-]\n    ;\n\nfragment\nFloatTypeSuffix\n    :   [fFdD]\n    ;\n\nfragment\nHexadecimalFloatingPointLiteral\n    :   HexSignificand BinaryExponent FloatTypeSuffix?\n    ;\n\nfragment\nHexSignificand\n    :   HexNumeral '.'?\n    |   '0' [xX] HexDigits? '.' HexDigits\n    ;\n\nfragment\nBinaryExponent\n    :   BinaryExponentIndicator SignedInteger\n    ;\n\nfragment\nBinaryExponentIndicator\n    :   [pP]\n    ;\n\n// §3.10.3 Boolean Literals\n\nBooleanLiteral\n    :   'true'\n    |   'false'\n    ;\n\n// §3.10.4 Character Literals\n\nCharacterLiteral\n    :   '\\'' SingleCharacter '\\''\n    |   '\\'' EscapeSequence '\\''\n    ;\n\nfragment\nSingleCharacter\n    :   ~['\\\\]\n    ;\n\n// §3.10.5 String Literals\n\nStringLiteral\n    :   '\"' StringCharacters? '\"'\n    ;\n\nfragment\nStringCharacters\n    :   StringCharacter+\n    ;\n\nfragment\nStringCharacter\n    :   ~[\"\\\\]\n    |   EscapeSequence\n    ;\n\n// §3.10.6 Escape Sequences for Character and String Literals\n\nfragment\nEscapeSequence\n    :   '\\\\' [btnfr\"'\\\\]\n    |   OctalEscape\n    |   UnicodeEscape\n    ;\n\nfragment\nOctalEscape\n    :   '\\\\' OctalDigit\n    |   '\\\\' OctalDigit OctalDigit\n    |   '\\\\' ZeroToThree OctalDigit OctalDigit\n    ;\n\nfragment\nUnicodeEscape\n    :   '\\\\' 'u' HexDigit HexDigit HexDigit HexDigit\n    ;\n\nfragment\nZeroToThree\n    :   [0-3]\n    ;\n\n// §3.10.7 The Null Literal\n\nNullLiteral\n    :   'null'\n    ;\n\n// §3.11 Separators\n\nLPAREN          : '(';\nRPAREN          : ')';\nLBRACE          : '{';\nRBRACE          : '}';\nLBRACK          : '[';\nRBRACK          : ']';\nSEMI            : ';';\nCOMMA           : ',';\nDOT             : '.';\n\n// §3.12 Operators\n\nASSIGN          : '=';\nGT              : '>';\nLT              : '<';\nBANG            : '!';\nTILDE           : '~';\nQUESTION        : '?';\nCOLON           : ':';\nEQUAL           : '==';\nLE              : '<=';\nGE              : '>=';\nNOTEQUAL        : '!=';\nAND             : '&&';\nOR              : '||';\nINC             : '++';\nDEC             : '--';\nADD             : '+';\nSUB             : '-';\nMUL             : '*';\nDIV             : '/';\nBITAND          : '&';\nBITOR           : '|';\nCARET           : '^';\nMOD             : '%';\n\nADD_ASSIGN      : '+=';\nSUB_ASSIGN      : '-=';\nMUL_ASSIGN      : '*=';\nDIV_ASSIGN      : '/=';\nAND_ASSIGN      : '&=';\nOR_ASSIGN       : '|=';\nXOR_ASSIGN      : '^=';\nMOD_ASSIGN      : '%=';\nLSHIFT_ASSIGN   : '<<=';\nRSHIFT_ASSIGN   : '>>=';\nURSHIFT_ASSIGN  : '>>>=';\n\n// §3.8 Identifiers (must appear after all keywords in the grammar)\n\nIdentifier\n    :   JavaLetter JavaLetterOrDigit*\n    ;\n\nfragment\nJavaLetter\n    :   [a-zA-Z$_] // these are the \"java letters\" below 0xFF\n    |   // covers all characters above 0xFF which are not a surrogate\n        ~[\\u0000-\\u00FF\\uD800-\\uDBFF]\n        {Character.isJavaIdentifierStart(_input.LA(-1))}?\n    |   // covers UTF-16 surrogate pairs encodings for U+10000 to U+10FFFF\n        [\\uD800-\\uDBFF] [\\uDC00-\\uDFFF]\n        {Character.isJavaIdentifierStart(Character.toCodePoint((char)_input.LA(-2), (char)_input.LA(-1)))}?\n    ;\n\nfragment\nJavaLetterOrDigit\n    :   [a-zA-Z0-9$_] // these are the \"java letters or digits\" below 0xFF\n    |   // covers all characters above 0xFF which are not a surrogate\n        ~[\\u0000-\\u00FF\\uD800-\\uDBFF]\n        {Character.isJavaIdentifierPart(_input.LA(-1))}?\n    |   // covers UTF-16 surrogate pairs encodings for U+10000 to U+10FFFF\n        [\\uD800-\\uDBFF] [\\uDC00-\\uDFFF]\n        {Character.isJavaIdentifierPart(Character.toCodePoint((char)_input.LA(-2), (char)_input.LA(-1)))}?\n    ;\n\n//\n// Additional symbols not defined in the lexical specification\n//\n\nAT : '@';\nELLIPSIS : '...';\n\n//\n// Whitespace and comments\n//\n\nWS  :  [ \\t\\r\\n\\u000C]+ -> skip\n    ;\n\nCOMMENT\n    :   '/*' .*? '*/' -> skip\n    ;\n\nLINE_COMMENT\n    :   '//' ~[\\r\\n]* -> skip\n    ;\n"
  },
  {
    "path": "services/src/main/java/org/fife/ui/rtextarea/Marker.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.fife.ui.rtextarea;\n\nimport org.fife.ui.rsyntaxtextarea.DocumentRange;\n\nimport java.util.List;\n\n/*\n * An utility class to call the restricted access methods of 'RTextArea'.\n *\n * JD-GUI uses two workarounds for RSyntaxTextArea:\n * - org.fife.ui.rtextarea.Marker\n * - org.jd.gui.view.component.RoundMarkErrorStrip\n */\npublic class Marker {\n    public static void markAll(RTextArea textArea, List<DocumentRange> ranges) {\n        textArea.markAll(ranges);\n    }\n\n    public static void clearMarkAllHighlights(RTextArea textArea) {\n        textArea.clearMarkAllHighlights();\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/model/container/ContainerEntryComparator.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.model.container;\n\nimport org.jd.gui.api.model.Container;\n\nimport java.util.Comparator;\n\n/**\n * Directories before files, sorted by path\n */\npublic class ContainerEntryComparator implements Comparator<Container.Entry> {\n    public static final ContainerEntryComparator COMPARATOR = new ContainerEntryComparator();\n\n    public int compare(Container.Entry e1, Container.Entry e2) {\n        if (e1.isDirectory()) {\n            if (!e2.isDirectory()) {\n                return -1;\n            }\n        } else {\n            if (e2.isDirectory()) {\n                return 1;\n            }\n        }\n        return e1.getPath().compareTo(e2.getPath());\n    }\n}"
  },
  {
    "path": "services/src/main/java/org/jd/gui/model/container/EarContainer.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.model.container;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\n\nimport java.nio.file.Path;\n\npublic class EarContainer extends GenericContainer {\n    public EarContainer(API api, Container.Entry parentEntry, Path rootPath) {\n        super(api, parentEntry, rootPath);\n    }\n\n    public String getType() { return \"ear\"; }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/model/container/GenericContainer.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.model.container;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.spi.ContainerFactory;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.nio.file.*;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Iterator;\n\npublic class GenericContainer implements Container {\n    protected static final long TIMESTAMP = System.currentTimeMillis();\n\n    protected static long tmpFileCounter = 0;\n\n    protected API api;\n    protected int rootNameCount;\n    protected Container.Entry root;\n\n    public GenericContainer(API api, Container.Entry parentEntry, Path rootPath) {\n        try {\n            URI uri = parentEntry.getUri();\n\n            this.api = api;\n            this.rootNameCount = rootPath.getNameCount();\n            this.root = new Entry(parentEntry, rootPath, new URI(uri.getScheme(), uri.getHost(), uri.getPath() + \"!/\", null)) {\n                public Entry newChildEntry(Path fsPath) {\n                    return new Entry(parent, fsPath, null);\n                }\n            };\n        } catch (URISyntaxException e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n    }\n\n    public String getType() { return \"generic\"; }\n    public Container.Entry getRoot() { return root; }\n\n    protected class Entry implements Container.Entry {\n        protected Container.Entry parent;\n        protected Path fsPath;\n        protected String strPath;\n        protected URI uri;\n        protected Boolean isDirectory;\n        protected Collection<Container.Entry> children;\n\n        public Entry(Container.Entry parent, Path fsPath, URI uri) {\n            this.parent = parent;\n            this.fsPath = fsPath;\n            this.strPath = null;\n            this.uri = uri;\n            this.isDirectory = null;\n            this.children = null;\n        }\n\n        public Entry newChildEntry(Path fsPath) { return new Entry(this, fsPath, null); }\n\n        public Container getContainer() { return GenericContainer.this; }\n        public Container.Entry getParent() { return parent; }\n\n        public URI getUri() {\n            if (uri == null) {\n                try {\n                    URI rootUri = root.getUri();\n                    uri = new URI(rootUri.getScheme(), rootUri.getHost(), rootUri.getPath() + getPath(), null);\n                } catch (URISyntaxException e) {\n                    assert ExceptionUtil.printStackTrace(e);\n                }\n            }\n            return uri;\n        }\n\n        public String getPath() {\n            if (strPath == null) {\n                int nameCount = fsPath.getNameCount();\n\n                if (rootNameCount == nameCount) {\n                    strPath = \"\";\n                } else {\n                    strPath = fsPath.subpath(rootNameCount, nameCount).toString().replace(fsPath.getFileSystem().getSeparator(), \"/\");\n\n                    int strPathLength = strPath.length();\n\n                    if ((strPathLength > 0) && strPath.charAt(strPathLength-1) == '/') {\n                        // Cut last separator\n                        strPath = strPath.substring(0, strPathLength-1);\n                    }\n                }\n            }\n            return strPath;\n        }\n\n        public boolean isDirectory() {\n            if (isDirectory == null) {\n                isDirectory = Boolean.valueOf(Files.isDirectory(fsPath));\n            }\n            return isDirectory;\n        }\n\n        public long length() {\n            try {\n                return Files.size(fsPath);\n            } catch (IOException e) {\n                assert ExceptionUtil.printStackTrace(e);\n                return -1L;\n            }\n        }\n\n        public InputStream getInputStream() {\n            try {\n                return Files.newInputStream(fsPath);\n            } catch (IOException e) {\n                assert ExceptionUtil.printStackTrace(e);\n                return null;\n            }\n        }\n\n        public Collection<Container.Entry> getChildren() {\n            if (children == null) {\n                try {\n                    if (Files.isDirectory(fsPath)) {\n                        children = loadChildrenFromDirectoryEntry();\n                    } else {\n                        children = loadChildrenFromFileEntry();\n                    }\n                } catch (IOException e) {\n                    assert ExceptionUtil.printStackTrace(e);\n                }\n            }\n            return children;\n        }\n\n        protected Collection<Container.Entry> loadChildrenFromDirectoryEntry() throws IOException {\n            try (DirectoryStream<Path> stream = Files.newDirectoryStream(fsPath)) {\n                ArrayList<Container.Entry> children = new ArrayList<>();\n                int parentNameCount = fsPath.getNameCount();\n\n                for (Path subPath : stream) {\n                    if (subPath.getNameCount() > parentNameCount) {\n                        children.add(newChildEntry(subPath));\n                    }\n                }\n\n                children.sort(ContainerEntryComparator.COMPARATOR);\n                return Collections.unmodifiableCollection(children);\n            }\n        }\n\n        protected Collection<Container.Entry> loadChildrenFromFileEntry() throws IOException {\n            StringBuilder suffix = new StringBuilder(\".\").append(TIMESTAMP).append('.').append(tmpFileCounter++).append('.').append(fsPath.getFileName().toString());\n            File tmpFile = File.createTempFile(\"jd-gui.tmp.\", suffix.toString());\n            Path tmpPath = Paths.get(tmpFile.toURI());\n\n            tmpFile.delete();\n            tmpFile.deleteOnExit();\n            Files.copy(fsPath, tmpPath);\n\n            FileSystem subFileSystem = FileSystems.newFileSystem(tmpPath, null);\n\n            if (subFileSystem != null) {\n                Iterator<Path> rootDirectories = subFileSystem.getRootDirectories().iterator();\n\n                if (rootDirectories.hasNext()) {\n                    Path rootPath = rootDirectories.next();\n                    ContainerFactory containerFactory = api.getContainerFactory(rootPath);\n\n                    if (containerFactory != null) {\n                        Container container = containerFactory.make(api, this, rootPath);\n\n                        if (container != null) {\n                            return container.getRoot().getChildren();\n                        }\n                    }\n                }\n            }\n\n            tmpFile.delete();\n            return Collections.emptyList();\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/model/container/JarContainer.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.model.container;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\n\nimport java.nio.file.Path;\n\npublic class JarContainer extends GenericContainer {\n    public JarContainer(API api, Container.Entry parentEntry, Path rootPath) {\n        super(api, parentEntry, rootPath);\n    }\n\n    public String getType() { return \"jar\"; }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/model/container/JavaModuleContainer.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.model.container;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\n\nimport java.nio.file.Path;\n\npublic class JavaModuleContainer extends GenericContainer {\n    public JavaModuleContainer(API api, Container.Entry parentEntry, Path rootPath) {\n        super(api, parentEntry, rootPath);\n    }\n\n    public String getType() { return \"jmod\"; }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/model/container/KarContainer.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.model.container;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\n\nimport java.nio.file.Path;\n\npublic class KarContainer extends GenericContainer {\n    public KarContainer(API api, Container.Entry parentEntry, Path rootPath) {\n        super(api, parentEntry, rootPath);\n    }\n\n    public String getType() { return \"kar\"; }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/model/container/WarContainer.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.model.container;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\n\nimport java.nio.file.Path;\n\npublic class WarContainer extends GenericContainer {\n    public WarContainer(API api, Container.Entry parentEntry, Path rootPath) {\n        super(api, parentEntry, rootPath);\n    }\n\n    public String getType() { return \"war\"; }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/actions/CopyQualifiedNameContextualActionsFactory.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.actions;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Type;\nimport org.jd.gui.spi.ContextualActionsFactory;\nimport org.jd.gui.spi.TypeFactory;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.awt.datatransfer.StringSelection;\nimport java.awt.event.ActionEvent;\nimport java.util.Collection;\nimport java.util.Collections;\n\npublic class CopyQualifiedNameContextualActionsFactory implements ContextualActionsFactory {\n\n    public Collection<Action> make(API api, Container.Entry entry, String fragment) {\n        return Collections.<Action>singletonList(new CopyQualifiedNameAction(api, entry, fragment));\n    }\n\n    public static class CopyQualifiedNameAction extends AbstractAction {\n        protected static final ImageIcon ICON = new ImageIcon(CopyQualifiedNameAction.class.getClassLoader().getResource(\"org/jd/gui/images/cpyqual_menu.png\"));\n\n        protected API api;\n        protected Container.Entry entry;\n        protected String fragment;\n\n        public CopyQualifiedNameAction(API api, Container.Entry entry, String fragment) {\n            this.api = api;\n            this.entry = entry;\n            this.fragment = fragment;\n\n            putValue(GROUP_NAME, \"Edit > CutCopyPaste\");\n            putValue(NAME, \"Copy Qualified Name\");\n            putValue(SMALL_ICON, ICON);\n        }\n\n        public void actionPerformed(ActionEvent e) {\n            TypeFactory typeFactory = api.getTypeFactory(entry);\n\n            if (typeFactory != null) {\n                Type type = typeFactory.make(api, entry, fragment);\n\n                if (type != null) {\n                    StringBuilder sb = new StringBuilder(type.getDisplayPackageName());\n\n                    if (sb.length() > 0) {\n                        sb.append('.');\n                    }\n\n                    sb.append(type.getDisplayTypeName());\n\n                    if (fragment != null) {\n                        int dashIndex = fragment.indexOf('-');\n\n                        if (dashIndex != -1) {\n                            int lastDashIndex = fragment.lastIndexOf('-');\n\n                            if (dashIndex == lastDashIndex) {\n                                // See jd.gui.api.feature.UriOpenable\n                                throw new InvalidFormatException(\"fragment: \" + fragment);\n                            } else {\n                                String name = fragment.substring(dashIndex + 1, lastDashIndex);\n                                String descriptor = fragment.substring(lastDashIndex + 1);\n\n                                if (descriptor.startsWith(\"(\")) {\n                                    for (Type.Method method : type.getMethods()) {\n                                        if (method.getName().equals(name) && method.getDescriptor().equals(descriptor)) {\n                                            sb.append('.').append(method.getDisplayName());\n                                            break;\n                                        }\n                                    }\n                                } else {\n                                    for (Type.Field field : type.getFields()) {\n                                        if (field.getName().equals(name) && field.getDescriptor().equals(descriptor)) {\n                                            sb.append('.').append(field.getDisplayName());\n                                            break;\n                                        }\n                                    }\n                                }\n                            }\n                        }\n                    }\n\n                    Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(sb.toString()), null);\n                    return;\n                }\n            }\n\n            // Create qualified name from URI\n            String path = entry.getUri().getPath();\n            String rootPath = entry.getContainer().getRoot().getUri().getPath();\n            String qualifiedName = path.substring(rootPath.length()).replace('/', '.');\n\n            if (qualifiedName.endsWith(\".class\")) {\n                qualifiedName = qualifiedName.substring(0, qualifiedName.length()-6);\n            }\n\n            Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(qualifiedName), null);\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/actions/InvalidFormatException.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.actions;\n\npublic class InvalidFormatException extends RuntimeException{\n    public InvalidFormatException(String message) { super(message); }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/container/EarContainerFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use, \n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.container;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.model.container.EarContainer;\nimport org.jd.gui.spi.ContainerFactory;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport java.nio.file.Files;\nimport java.nio.file.InvalidPathException;\nimport java.nio.file.Path;\n\npublic class EarContainerFactoryProvider implements ContainerFactory {\n    @Override\n    public String getType() { return \"ear\"; }\n\n    @Override\n    public boolean accept(API api, Path rootPath) {\n        if (rootPath.toUri().toString().toLowerCase().endsWith(\".ear!/\")) {\n            return true;\n        } else {\n            // Extension: accept uncompressed EAR file containing a folder 'META-INF/application.xml'\n            try {\n                return rootPath.getFileSystem().provider().getScheme().equals(\"file\") && Files.exists(rootPath.resolve(\"META-INF/application.xml\"));\n            } catch (InvalidPathException e) {\n                assert ExceptionUtil.printStackTrace(e);\n                return false;\n            }\n        }\n    }\n\n    @Override\n    public Container make(API api, Container.Entry parentEntry, Path rootPath) {\n        return new EarContainer(api, parentEntry, rootPath);\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/container/GenericContainerFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use, \n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.container;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.model.container.GenericContainer;\nimport org.jd.gui.spi.ContainerFactory;\n\nimport java.nio.file.Path;\n\npublic class GenericContainerFactoryProvider implements ContainerFactory {\n    @Override\n    public String getType() { return \"generic\"; }\n\n    @Override\n    public boolean accept(API api, Path rootPath) { return true; }\n\n    @Override\n    public Container make(API api, Container.Entry parentEntry, Path rootPath) {\n        return new GenericContainer(api, parentEntry, rootPath);\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/container/JarContainerFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use, \n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.container;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.model.container.JarContainer;\nimport org.jd.gui.spi.ContainerFactory;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport java.nio.file.Files;\nimport java.nio.file.InvalidPathException;\nimport java.nio.file.Path;\n\npublic class JarContainerFactoryProvider implements ContainerFactory {\n    @Override\n    public String getType() { return \"jar\"; }\n\n    @Override\n    public boolean accept(API api, Path rootPath) {\n        if (rootPath.toUri().toString().toLowerCase().endsWith(\".jar!/\")) {\n            // Specification: http://docs.oracle.com/javase/6/docs/technotes/guides/jar/jar.html\n            return true;\n        } else {\n            // Extension: accept uncompressed JAR file containing a folder 'META-INF'\n            try {\n                return rootPath.getFileSystem().provider().getScheme().equals(\"file\") && Files.exists(rootPath.resolve(\"META-INF\"));\n            } catch (InvalidPathException e) {\n                assert ExceptionUtil.printStackTrace(e);\n                return false;\n            }\n        }\n    }\n\n    @Override\n    public Container make(API api, Container.Entry parentEntry, Path rootPath) {\n        return new JarContainer(api, parentEntry, rootPath);\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/container/JavaModuleContainerFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use, \n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.container;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.model.container.JavaModuleContainer;\nimport org.jd.gui.spi.ContainerFactory;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport java.nio.file.Files;\nimport java.nio.file.InvalidPathException;\nimport java.nio.file.Path;\n\npublic class JavaModuleContainerFactoryProvider implements ContainerFactory {\n    @Override\n    public String getType() { return \"jmod\"; }\n\n    @Override\n    public boolean accept(API api, Path rootPath) {\n        if (rootPath.toUri().toString().toLowerCase().endsWith(\".jmod!/\")) {\n            return true;\n        } else {\n            // Extension: accept uncompressed JMOD file containing a folder 'classes'\n            try {\n                return rootPath.getFileSystem().provider().getScheme().equals(\"file\") && Files.exists(rootPath.resolve(\"classes\"));\n            } catch (InvalidPathException e) {\n                assert ExceptionUtil.printStackTrace(e);\n                return false;\n            }\n        }\n    }\n\n    @Override\n    public Container make(API api, Container.Entry parentEntry, Path rootPath) {\n        return new JavaModuleContainer(api, parentEntry, rootPath);\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/container/KarContainerFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use, \n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.container;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.model.container.KarContainer;\nimport org.jd.gui.spi.ContainerFactory;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport java.nio.file.Files;\nimport java.nio.file.InvalidPathException;\nimport java.nio.file.Path;\n\npublic class KarContainerFactoryProvider implements ContainerFactory {\n    @Override\n    public String getType() { return \"kar\"; }\n\n    @Override\n    public boolean accept(API api, Path rootPath) {\n        if (rootPath.toUri().toString().toLowerCase().endsWith(\".kar!/\")) {\n            return true;\n        } else {\n            // Extension: accept uncompressed KAR file containing a folder 'repository'\n            try {\n                return rootPath.getFileSystem().provider().getScheme().equals(\"file\") && Files.exists(rootPath.resolve(\"repository\"));\n            } catch (InvalidPathException e) {\n                assert ExceptionUtil.printStackTrace(e);\n                return false;\n            }\n        }\n    }\n\n    @Override\n    public Container make(API api, Container.Entry parentEntry, Path rootPath) {\n        return new KarContainer(api, parentEntry, rootPath);\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/container/WarContainerFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use, \n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.container;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.model.container.WarContainer;\nimport org.jd.gui.spi.ContainerFactory;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport java.nio.file.Files;\nimport java.nio.file.InvalidPathException;\nimport java.nio.file.Path;\n\npublic class WarContainerFactoryProvider implements ContainerFactory {\n    @Override\n    public String getType() { return \"war\"; }\n\n    @Override\n    public boolean accept(API api, Path rootPath) {\n        if (rootPath.toUri().toString().toLowerCase().endsWith(\".war!/\")) {\n            return true;\n        } else {\n            // Extension: accept uncompressed WAR file containing a folder 'WEB-INF'\n            try {\n                return rootPath.getFileSystem().provider().getScheme().equals(\"file\") && Files.exists(rootPath.resolve(\"WEB-INF\"));\n            } catch (InvalidPathException e) {\n                assert ExceptionUtil.printStackTrace(e);\n                return false;\n            }\n        }\n    }\n\n    @Override\n    public Container make(API api, Container.Entry parentEntry, Path rootPath) {\n        return new WarContainer(api, parentEntry, rootPath);\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/fileloader/AarFileLoaderProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use, \n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.fileloader;\n\nimport org.jd.gui.api.API;\n\nimport java.io.File;\n\npublic class AarFileLoaderProvider extends ZipFileLoaderProvider {\n    protected static final String[] EXTENSIONS = { \"aar\" };\n\n    @Override public String[] getExtensions() { return EXTENSIONS; }\n    @Override public String getDescription() { return \"Android archive files (*.aar)\"; }\n\n    @Override\n    public boolean accept(API api, File file) {\n        return file.exists() && file.isFile() && file.canRead() && file.getName().toLowerCase().endsWith(\".aar\");\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/fileloader/AbstractFileLoaderProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.fileloader;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.TreeNodeData;\nimport org.jd.gui.spi.ContainerFactory;\nimport org.jd.gui.spi.FileLoader;\nimport org.jd.gui.spi.PanelFactory;\nimport org.jd.gui.spi.TreeNodeFactory;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport javax.swing.*;\nimport java.io.*;\nimport java.net.URI;\nimport java.nio.file.Path;\nimport java.util.Collection;\nimport java.util.Collections;\n\npublic abstract class AbstractFileLoaderProvider implements FileLoader {\n    protected <T extends JComponent & UriGettable> T load(API api, File file, Path rootPath) {\n        ContainerEntry parentEntry = new ContainerEntry(file);\n        ContainerFactory containerFactory = api.getContainerFactory(rootPath);\n\n        if (containerFactory != null) {\n            Container container = containerFactory.make(api, parentEntry, rootPath);\n\n            if (container != null) {\n                parentEntry.setChildren(container.getRoot().getChildren());\n\n                PanelFactory panelFactory = api.getMainPanelFactory(container);\n\n                if (panelFactory != null) {\n                    T mainPanel = panelFactory.make(api, container);\n\n                    if (mainPanel != null) {\n                        TreeNodeFactory treeNodeFactory = api.getTreeNodeFactory(parentEntry);\n                        Object data = (treeNodeFactory != null) ? treeNodeFactory.make(api, parentEntry).getUserObject() : null;\n                        Icon icon = (data instanceof TreeNodeData) ? ((TreeNodeData)data).getIcon() : null;\n                        String location = file.getPath();\n\n                        api.addPanel(file.getName(), icon, \"Location: \" + location, mainPanel);\n                        return mainPanel;\n                    }\n                }\n            }\n        }\n\n        return null;\n    }\n\n    protected static class ContainerEntry implements Container.Entry {\n        protected static final Container PARENT_CONTAINER = new Container() {\n            @Override public String getType() { return \"generic\"; }\n            @Override public Container.Entry getRoot() { return null; }\n        };\n\n        protected Collection<Container.Entry> children = Collections.emptyList();\n        protected File file;\n        protected URI uri;\n        protected String path;\n\n        public ContainerEntry(File file) {\n            this.file = file;\n            this.uri = file.toURI();\n            this.path = uri.getPath();\n\n            if (path.endsWith(\"/\")) {\n                path = path.substring(0, path.length() - 1);\n            }\n        }\n\n        @Override public Container getContainer() { return PARENT_CONTAINER; }\n        @Override public Container.Entry getParent() { return null; }\n        @Override public URI getUri() { return uri; }\n        @Override public String getPath() { return path; }\n        @Override public boolean isDirectory() { return file.isDirectory(); }\n        @Override public long length() { return file.length(); }\n        @Override public Collection<Container.Entry> getChildren() { return children; }\n\n        @Override\n        public InputStream getInputStream() {\n            try {\n                return new BufferedInputStream(new FileInputStream(file));\n            } catch (FileNotFoundException e) {\n                assert ExceptionUtil.printStackTrace(e);\n                return null;\n            }\n        }\n\n        public void setChildren(Collection<Container.Entry> children) {\n            this.children = children;\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/fileloader/AbstractTypeFileLoaderProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use, \n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.fileloader;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.UriOpenable;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport javax.swing.*;\nimport java.io.File;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.nio.file.Paths;\n\npublic abstract class AbstractTypeFileLoaderProvider extends AbstractFileLoaderProvider {\n    protected boolean load(API api, File file, String pathInFile) {\n        // Search root path\n        String pathSuffix = pathInFile;\n        String path = file.getPath();\n\n        while (! path.endsWith(pathSuffix)) {\n            int index = pathSuffix.indexOf(File.separator);\n\n            if (index == -1) {\n                pathSuffix = \"\";\n            } else {\n                pathSuffix = pathSuffix.substring(index+1);\n            }\n        }\n\n        if (! pathSuffix.isEmpty()) {\n            // Init root file\n            File rootFile = file;\n            int index = pathSuffix.indexOf(File.separator);\n\n            while (index != -1) {\n                rootFile = rootFile.getParentFile();\n                pathSuffix = pathSuffix.substring(index+1);\n                index = pathSuffix.indexOf(File.separator);\n            }\n            rootFile = rootFile.getParentFile();\n\n            // Create panel\n            JComponent mainPanel = load(api, rootFile, Paths.get(rootFile.toURI()));\n\n            if (mainPanel instanceof UriOpenable) {\n                try {\n                    // Open page\n                    pathSuffix = file.getAbsolutePath().substring(rootFile.getAbsolutePath().length()).replace(File.separator, \"/\");\n                    URI rootUri = rootFile.toURI();\n                    URI uri = new URI(rootUri.getScheme(), rootUri.getHost(), rootUri.getPath() + '!' + pathSuffix, null);\n                    ((UriOpenable)mainPanel).openUri(uri);\n                    return true;\n                } catch (URISyntaxException e) {\n                    assert ExceptionUtil.printStackTrace(e);\n                }\n            } else {\n                return mainPanel != null;\n            }\n        }\n\n        return false;\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/fileloader/ClassFileLoaderProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use, \n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.fileloader;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.objectweb.asm.ClassReader;\n\nimport java.io.BufferedInputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\n\npublic class ClassFileLoaderProvider extends AbstractTypeFileLoaderProvider {\n    protected static final String[] EXTENSIONS = { \"class\" };\n\n    @Override public String[] getExtensions() { return EXTENSIONS; }\n    @Override public String getDescription() { return \"Class files (*.class)\"; }\n\n    @Override\n    public boolean accept(API api, File file) {\n        return file.exists() && file.isFile() && file.canRead() && file.getName().toLowerCase().endsWith(\".class\");\n    }\n\n    @Override\n    public boolean load(API api, File file) {\n        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {\n            ClassReader classReader = new ClassReader(bis);\n            String pathInFile = classReader.getClassName().replace(\"/\", File.separator) + \".class\";\n\n            return load(api, file, pathInFile);\n        } catch (IOException e) {\n            assert ExceptionUtil.printStackTrace(e);\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/fileloader/EarFileLoaderProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use, \n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.fileloader;\n\nimport org.jd.gui.api.API;\n\nimport java.io.File;\n\npublic class EarFileLoaderProvider extends ZipFileLoaderProvider {\n    protected static final String[] EXTENSIONS = { \"ear\" };\n\n    @Override public String[] getExtensions() { return EXTENSIONS; }\n    @Override public String getDescription() { return \"Enterprise application archive files (*.ear)\"; }\n\n    @Override\n    public boolean accept(API api, File file) {\n        return file.exists() && file.isFile() && file.canRead() && file.getName().toLowerCase().endsWith(\".ear\");\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/fileloader/JarFileLoaderProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use, \n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.fileloader;\n\nimport org.jd.gui.api.API;\n\nimport java.io.File;\n\npublic class JarFileLoaderProvider extends ZipFileLoaderProvider {\n    protected static final String[] EXTENSIONS = { \"jar\" };\n\n    @Override public String[] getExtensions() { return EXTENSIONS; }\n    @Override public String getDescription() { return \"Java archive files (*.jar)\"; }\n\n    @Override\n    public boolean accept(API api, File file) {\n        return file.exists() && file.isFile() && file.canRead() && file.getName().toLowerCase().endsWith(\".jar\");\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/fileloader/JavaFileLoaderProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use, \n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.fileloader;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.util.io.TextReader;\n\nimport java.io.File;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\npublic class JavaFileLoaderProvider extends AbstractTypeFileLoaderProvider {\n    protected static final String[] EXTENSIONS = { \"java\" };\n\n    @Override public String[] getExtensions() { return EXTENSIONS; }\n    @Override public String getDescription() { return \"Java files (*.java)\"; }\n\n    @Override\n    public boolean accept(API api, File file) {\n        return file.exists() && file.isFile() && file.canRead() && file.getName().toLowerCase().endsWith(\".java\");\n    }\n\n    @Override\n    public boolean load(API api, File file) {\n        String text = TextReader.getText(file);\n        Pattern pattern = Pattern.compile(\"(?s)(.*\\\\s)?package\\\\s+(\\\\S+)\\\\s*;.*\");\n        Matcher matcher = pattern.matcher(text);\n\n        if (matcher.matches()) {\n            // Package name found\n            String pathInFile = matcher.group(2).replace(\".\", File.separator) + File.separator + file.getName();\n\n            return load(api, file, pathInFile);\n        } else {\n            // Package name not found\n            return load(api, file, file.getName());\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/fileloader/JavaModuleFileLoaderProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use, \n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.fileloader;\n\nimport org.jd.gui.api.API;\n\nimport java.io.File;\n\npublic class JavaModuleFileLoaderProvider extends ZipFileLoaderProvider {\n    protected static final String[] EXTENSIONS = { \"jmod\" };\n\n    @Override public String[] getExtensions() { return EXTENSIONS; }\n    @Override public String getDescription() { return \"Java module files (*.jmod)\"; }\n\n    @Override\n    public boolean accept(API api, File file) {\n        return file.exists() && file.isFile() && file.canRead() && file.getName().toLowerCase().endsWith(\".jmod\");\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/fileloader/KarFileLoaderProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use, \n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.fileloader;\n\nimport org.jd.gui.api.API;\n\nimport java.io.File;\n\npublic class KarFileLoaderProvider extends ZipFileLoaderProvider {\n    protected static final String[] EXTENSIONS = { \"kar\" };\n\n    @Override public String[] getExtensions() { return EXTENSIONS; }\n    @Override public String getDescription() { return \"Karaf archive files (*.kar)\"; }\n\n    @Override\n    public boolean accept(API api, File file) {\n        return file.exists() && file.isFile() && file.canRead() && file.getName().toLowerCase().endsWith(\".kar\");\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/fileloader/LogFileLoaderProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use, \n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.fileloader;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.util.io.TextReader;\nimport org.jd.gui.view.component.LogPage;\n\nimport java.io.File;\n\npublic class LogFileLoaderProvider extends ZipFileLoaderProvider {\n    protected static final String[] EXTENSIONS = { \"log\" };\n\n    @Override public String[] getExtensions() { return EXTENSIONS; }\n    @Override public String getDescription() { return \"Log files (*.log)\"; }\n\n    @Override\n    public boolean accept(API api, File file) {\n        return file.exists() && file.isFile() && file.canRead() && file.getName().toLowerCase().endsWith(\".log\");\n    }\n\n    @Override\n    public boolean load(API api, File file) {\n        api.addPanel(file.getName(), null, \"Location: \" + file.getAbsolutePath(), new LogPage(api, file.toURI(), TextReader.getText(file)));\n        return true;\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/fileloader/WarFileLoaderProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use, \n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.fileloader;\n\nimport org.jd.gui.api.API;\n\nimport java.io.File;\n\npublic class WarFileLoaderProvider extends ZipFileLoaderProvider {\n    protected static final String[] EXTENSIONS = { \"war\" };\n\n    @Override public String[] getExtensions() { return EXTENSIONS; }\n    @Override public String getDescription() { return \"Web application archive files (*.war)\"; }\n\n    @Override\n    public boolean accept(API api, File file) {\n        return file.exists() && file.isFile() && file.canRead() && file.getName().toLowerCase().endsWith(\".war\");\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/fileloader/ZipFileLoaderProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use, \n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.fileloader;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.nio.file.FileSystem;\nimport java.nio.file.FileSystemNotFoundException;\nimport java.nio.file.FileSystems;\nimport java.nio.file.Path;\nimport java.util.Collections;\nimport java.util.Iterator;\n\npublic class ZipFileLoaderProvider extends AbstractFileLoaderProvider {\n    protected static final String[] EXTENSIONS = { \"zip\" };\n\n    @Override public String[] getExtensions() { return EXTENSIONS; }\n    @Override public String getDescription() { return \"Zip files (*.zip)\"; }\n\n    @Override\n    public boolean accept(API api, File file) {\n        return file.exists() && file.isFile() && file.canRead() && file.getName().toLowerCase().endsWith(\".zip\");\n    }\n\n    @Override\n    public boolean load(API api, File file) {\n        try {\n            URI fileUri = file.toURI();\n            URI uri = new URI(\"jar:\" + fileUri.getScheme(), fileUri.getHost(), fileUri.getPath() + \"!/\", null);\n            FileSystem fileSystem;\n\n            try {\n                fileSystem = FileSystems.getFileSystem(uri);\n            } catch (FileSystemNotFoundException e) {\n                fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap());\n            }\n\n            if (fileSystem != null) {\n                Iterator<Path> rootDirectories = fileSystem.getRootDirectories().iterator();\n\n                if (rootDirectories.hasNext()) {\n                    return load(api, file, rootDirectories.next()) != null;\n                }\n            }\n        } catch (URISyntaxException|IOException e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n\n        return false;\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/indexer/AbstractIndexerProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.indexer;\n\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.spi.Indexer;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.*;\nimport java.util.regex.Pattern;\n\npublic abstract class AbstractIndexerProvider implements Indexer {\n    protected List<String> externalSelectors;\n    protected Pattern externalPathPattern;\n\n    /**\n     * Initialize \"selectors\" and \"pathPattern\" with optional external properties file\n     */\n    public AbstractIndexerProvider() {\n        Properties properties = new Properties();\n        Class clazz = this.getClass();\n\n        try (InputStream is = clazz.getClassLoader().getResourceAsStream(clazz.getName().replace('.', '/') + \".properties\")) {\n            if (is != null) {\n                properties.load(is);\n            }\n        } catch (IOException e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n\n        init(properties);\n    }\n\n    protected void init(Properties properties) {\n        String selectors = properties.getProperty(\"selectors\");\n\n        if (selectors != null) {\n            externalSelectors = Arrays.asList(selectors.split(\",\"));\n        }\n\n        String pathRegExp = properties.getProperty(\"pathRegExp\");\n\n        if (pathRegExp != null) {\n            externalPathPattern = Pattern.compile(pathRegExp);\n        }\n    }\n\n    protected String[] appendSelectors(String selector) {\n        if (externalSelectors == null) {\n            return new String[] { selector };\n        } else {\n            int size = externalSelectors.size();\n            String[] array = new String[size+1];\n            externalSelectors.toArray(array);\n            array[size] = selector;\n            return array;\n        }\n    }\n\n    protected String[] appendSelectors(String... selectors) {\n        if (externalSelectors == null) {\n            return selectors;\n        } else {\n            int size = externalSelectors.size();\n            String[] array = new String[size+selectors.length];\n            externalSelectors.toArray(array);\n            System.arraycopy(selectors, 0, array, size, selectors.length);\n            return array;\n        }\n    }\n\n    @Override public Pattern getPathPattern() { return externalPathPattern; }\n\n    @SuppressWarnings(\"unchecked\")\n    protected static void addToIndexes(Indexes indexes, String indexName, Set<String> set, Container.Entry entry) {\n        if (set.size() > 0) {\n            Map<String, Collection> index = indexes.getIndex(indexName);\n\n            for (String key : set) {\n                index.get(key).add(entry);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/indexer/ClassFileIndexerProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.indexer;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.objectweb.asm.*;\nimport org.objectweb.asm.signature.SignatureReader;\nimport org.objectweb.asm.signature.SignatureVisitor;\n\nimport java.io.InputStream;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.Map;\nimport java.util.regex.Pattern;\n\nimport static org.objectweb.asm.ClassReader.*;\n\n/**\n * Unsafe thread implementation of class file indexer.\n */\npublic class ClassFileIndexerProvider extends AbstractIndexerProvider {\n    protected HashSet<String> typeDeclarationSet = new HashSet<>();\n    protected HashSet<String> constructorDeclarationSet = new HashSet<>();\n    protected HashSet<String> methodDeclarationSet = new HashSet<>();\n    protected HashSet<String> fieldDeclarationSet = new HashSet<>();\n    protected HashSet<String> typeReferenceSet = new HashSet<>();\n    protected HashSet<String> constructorReferenceSet = new HashSet<>();\n    protected HashSet<String> methodReferenceSet = new HashSet<>();\n    protected HashSet<String> fieldReferenceSet = new HashSet<>();\n    protected HashSet<String> stringSet = new HashSet<>();\n    protected HashSet<String> superTypeNameSet = new HashSet<>();\n    protected HashSet<String> descriptorSet = new HashSet<>();\n\n    protected ClassIndexer classIndexer = new ClassIndexer();\n    protected SignatureIndexer signatureIndexer = new SignatureIndexer();\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.class\"); }\n\n    @Override\n    public Pattern getPathPattern() {\n        if (externalPathPattern == null) {\n            return Pattern.compile(\"^((?!module-info\\\\.class).)*$\");\n        } else {\n            return externalPathPattern;\n        }\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public void index(API api, Container.Entry entry, Indexes indexes) {\n        // Cleaning sets...\n        typeDeclarationSet.clear();\n        constructorDeclarationSet.clear();\n        methodDeclarationSet.clear();\n        fieldDeclarationSet.clear();\n        typeReferenceSet.clear();\n        constructorReferenceSet.clear();\n        methodReferenceSet.clear();\n        fieldReferenceSet.clear();\n        stringSet.clear();\n        superTypeNameSet.clear();\n        descriptorSet.clear();\n\n        try (InputStream inputStream = entry.getInputStream()) {\n            // Index field, method, interfaces & super type\n            ClassReader classReader = new ClassReader(inputStream);\n            classReader.accept(classIndexer, SKIP_CODE|SKIP_DEBUG|SKIP_FRAMES);\n\n            // Index descriptors\n            for (String descriptor : descriptorSet) {\n                new SignatureReader(descriptor).accept(signatureIndexer);\n            }\n\n            // Index references\n            char[] buffer = new char[classReader.getMaxStringLength()];\n\n            for (int i=classReader.getItemCount()-1; i>0; i--) {\n                int startIndex = classReader.getItem(i);\n\n                if (startIndex != 0) {\n                    int tag = classReader.readByte(startIndex-1);\n\n                    switch (tag) {\n                        case 7: // CONSTANT_Class\n                            String className = classReader.readUTF8(startIndex, buffer);\n                            if (className.startsWith(\"[\")) {\n                                new SignatureReader(className).acceptType(signatureIndexer);\n                            } else {\n                                typeReferenceSet.add(className);\n                            }\n                            break;\n                        case 8: // CONSTANT_String\n                            String str = classReader.readUTF8(startIndex, buffer);\n                            stringSet.add(str);\n                            break;\n                        case 9: // CONSTANT_Fieldref\n                            int nameAndTypeItem = classReader.readUnsignedShort(startIndex+2);\n                            int nameAndTypeIndex = classReader.getItem(nameAndTypeItem);\n                            tag = classReader.readByte(nameAndTypeIndex-1);\n                            if (tag == 12) { // CONSTANT_NameAndType\n                                String fieldName = classReader.readUTF8(nameAndTypeIndex, buffer);\n                                fieldReferenceSet.add(fieldName);\n                            }\n                            break;\n                        case 10: // CONSTANT_Methodref:\n                        case 11: // CONSTANT_InterfaceMethodref:\n                            nameAndTypeItem = classReader.readUnsignedShort(startIndex+2);\n                            nameAndTypeIndex = classReader.getItem(nameAndTypeItem);\n                            tag = classReader.readByte(nameAndTypeIndex-1);\n                            if (tag == 12) { // CONSTANT_NameAndType\n                                String methodName = classReader.readUTF8(nameAndTypeIndex, buffer);\n                                if (\"<init>\".equals(methodName)) {\n                                    int classItem = classReader.readUnsignedShort(startIndex);\n                                    int classIndex = classReader.getItem(classItem);\n                                    className = classReader.readUTF8(classIndex, buffer);\n                                    constructorReferenceSet.add(className);\n                                } else {\n                                    methodReferenceSet.add(methodName);\n                                }\n                            }\n                            break;\n                    }\n                }\n            }\n\n            String typeName = classIndexer.name;\n\n            // Append sets to indexes\n            addToIndexes(indexes, \"typeDeclarations\", typeDeclarationSet, entry);\n            addToIndexes(indexes, \"constructorDeclarations\", constructorDeclarationSet, entry);\n            addToIndexes(indexes, \"methodDeclarations\", methodDeclarationSet, entry);\n            addToIndexes(indexes, \"fieldDeclarations\", fieldDeclarationSet, entry);\n            addToIndexes(indexes, \"typeReferences\", typeReferenceSet, entry);\n            addToIndexes(indexes, \"constructorReferences\", constructorReferenceSet, entry);\n            addToIndexes(indexes, \"methodReferences\", methodReferenceSet, entry);\n            addToIndexes(indexes, \"fieldReferences\", fieldReferenceSet, entry);\n            addToIndexes(indexes, \"strings\", stringSet, entry);\n\n            // Populate map [super type name : [sub type name]]\n            if (superTypeNameSet.size() > 0) {\n                Map<String, Collection> index = indexes.getIndex(\"subTypeNames\");\n\n                for (String superTypeName : superTypeNameSet) {\n                    index.get(superTypeName).add(typeName);\n                }\n            }\n        } catch (Exception e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n    }\n\n    protected class ClassIndexer extends ClassVisitor {\n        protected AnnotationIndexer annotationIndexer = new AnnotationIndexer();\n        protected FieldIndexer fieldIndexer = new FieldIndexer(annotationIndexer);\n        protected MethodIndexer methodIndexer = new MethodIndexer(annotationIndexer);\n\n        protected String name;\n\n        public ClassIndexer() { super(Opcodes.ASM7); }\n\n        @Override\n        public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n            this.name = name;\n            typeDeclarationSet.add(name);\n\n            if (superName != null) {\n                superTypeNameSet.add(superName);\n            }\n\n            if (interfaces != null) {\n                for (int i=interfaces.length-1; i>=0; i--) {\n                    superTypeNameSet.add(interfaces[i]);\n                }\n            }\n        }\n\n        @Override\n        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {\n            descriptorSet.add(desc);\n            return annotationIndexer;\n        }\n\n        @Override\n        public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {\n            descriptorSet.add(desc);\n            return annotationIndexer;\n        }\n\n        @Override\n        public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {\n            fieldDeclarationSet.add(name);\n            descriptorSet.add(signature==null ? desc : signature);\n            return fieldIndexer;\n        }\n\n        @Override\n        public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {\n            if (\"<init>\".equals(name)) {\n                constructorDeclarationSet.add(this.name);\n            } else if (! \"<clinit>\".equals(name)) {\n                methodDeclarationSet.add(name);\n            }\n\n            descriptorSet.add(signature==null ? desc : signature);\n\n            if (exceptions != null) {\n                for (int i=exceptions.length-1; i>=0; i--) {\n                    typeReferenceSet.add(exceptions[i]);\n                }\n            }\n            return methodIndexer;\n        }\n    }\n\n    protected class SignatureIndexer extends SignatureVisitor {\n        SignatureIndexer() { super(Opcodes.ASM7); }\n\n        @Override public void visitClassType(String name) { typeReferenceSet.add(name); }\n    }\n\n    protected class AnnotationIndexer extends AnnotationVisitor {\n        public AnnotationIndexer() { super(Opcodes.ASM7); }\n\n        @Override public void visitEnum(String name, String desc, String value) { descriptorSet.add(desc); }\n\n        @Override\n        public AnnotationVisitor visitAnnotation(String name, String desc) {\n            descriptorSet.add(desc);\n            return this;\n        }\n    }\n\n    protected class FieldIndexer extends FieldVisitor {\n        protected AnnotationIndexer annotationIndexer;\n\n        public FieldIndexer(AnnotationIndexer annotationIndexer) {\n            super(Opcodes.ASM7);\n            this.annotationIndexer = annotationIndexer;\n        }\n\n        @Override\n        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {\n            descriptorSet.add(desc);\n            return annotationIndexer;\n        }\n\n        @Override\n        public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {\n            descriptorSet.add(desc);\n            return annotationIndexer;\n        }\n    }\n\n    protected class MethodIndexer extends MethodVisitor {\n        protected AnnotationIndexer annotationIndexer;\n\n        public MethodIndexer(AnnotationIndexer annotationIndexer) {\n            super(Opcodes.ASM7);\n            this.annotationIndexer = annotationIndexer;\n        }\n\n        @Override\n        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {\n            descriptorSet.add(desc);\n            return annotationIndexer;\n        }\n\n        @Override\n        public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) {\n            descriptorSet.add(desc);\n            return annotationIndexer;\n        }\n\n        @Override\n        public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {\n            descriptorSet.add(desc);\n            return annotationIndexer;\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/indexer/DirectoryIndexerProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.indexer;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.spi.Indexer;\n\npublic class DirectoryIndexerProvider extends AbstractIndexerProvider {\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:dir:*\"); }\n\n    @Override\n    public void index(API api, Container.Entry entry, Indexes indexes) {\n        int depth = 15;\n\n        try {\n            depth = Integer.valueOf(api.getPreferences().get(\"DirectoryIndexerPreferences.maximumDepth\"));\n        } catch (NumberFormatException ignore) {\n        }\n\n        index(api, entry, indexes, depth);\n    }\n\n    public void index(API api, Container.Entry entry, Indexes indexes, int depth) {\n        if (depth-- > 0) {\n            for (Container.Entry e : entry.getChildren()) {\n                if (e.isDirectory()) {\n                    index(api, e, indexes, depth);\n                } else {\n                    Indexer indexer = api.getIndexer(e);\n\n                    if (indexer != null) {\n                        indexer.index(api, e, indexes);\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/indexer/EjbJarXmlFileIndexerProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.indexer;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.util.io.TextReader;\nimport org.jd.gui.util.xml.AbstractXmlPathFinder;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Map;\n\npublic class EjbJarXmlFileIndexerProvider extends XmlBasedFileIndexerProvider {\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:META-INF/ejb-jar.xml\"); }\n\n    @Override\n    public void index(API api, Container.Entry entry, Indexes indexes) {\n        super.index(api, entry, indexes);\n\n        new EjbJarXmlPathFinder(entry, indexes).find(TextReader.getText(entry.getInputStream()));\n    }\n\n    public static class EjbJarXmlPathFinder extends AbstractXmlPathFinder {\n        protected Container.Entry entry;\n        protected Map<String, Collection> index;\n\n        public EjbJarXmlPathFinder(Container.Entry entry, Indexes indexes) {\n            super(Arrays.asList(\n                \"ejb-jar/assembly-descriptor/application-exception/exception-class\",\n                \"ejb-jar/assembly-descriptor/interceptor-binding/interceptor-class\",\n\n                \"ejb-jar/enterprise-beans/entity/home\",\n                \"ejb-jar/enterprise-beans/entity/remote\",\n                \"ejb-jar/enterprise-beans/entity/ejb-class\",\n                \"ejb-jar/enterprise-beans/entity/prim-key-class\",\n\n                \"ejb-jar/enterprise-beans/message-driven/ejb-class\",\n                \"ejb-jar/enterprise-beans/message-driven/messaging-type\",\n                \"ejb-jar/enterprise-beans/message-driven/resource-ref/injection-target/injection-target-class\",\n                \"ejb-jar/enterprise-beans/message-driven/resource-env-ref/injection-target/injection-target-class\",\n\n                \"ejb-jar/enterprise-beans/session/home\",\n                \"ejb-jar/enterprise-beans/session/local\",\n                \"ejb-jar/enterprise-beans/session/remote\",\n                \"ejb-jar/enterprise-beans/session/business-local\",\n                \"ejb-jar/enterprise-beans/session/business-remote\",\n                \"ejb-jar/enterprise-beans/session/service-endpoint\",\n                \"ejb-jar/enterprise-beans/session/ejb-class\",\n                \"ejb-jar/enterprise-beans/session/ejb-ref/home\",\n                \"ejb-jar/enterprise-beans/session/ejb-ref/remote\",\n\n                \"ejb-jar/interceptors/interceptor/around-invoke/class\",\n                \"ejb-jar/interceptors/interceptor/ejb-ref/home\",\n                \"ejb-jar/interceptors/interceptor/ejb-ref/remote\",\n                \"ejb-jar/interceptors/interceptor/interceptor-class\"\n            ));\n            this.entry = entry;\n            this.index = indexes.getIndex(\"typeReferences\");\n        }\n\n        @Override\n        @SuppressWarnings(\"unchecked\")\n        public void handle(String path, String text, int position) {\n            index.get(text.replace(\".\", \"/\")).add(entry);\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/indexer/JavaFileIndexerProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.indexer;\n\nimport org.antlr.v4.runtime.ANTLRInputStream;\nimport org.antlr.v4.runtime.ParserRuleContext;\nimport org.antlr.v4.runtime.tree.ParseTree;\nimport org.antlr.v4.runtime.tree.TerminalNode;\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.util.parser.antlr.ANTLRJavaParser;\nimport org.jd.gui.util.parser.antlr.AbstractJavaListener;\nimport org.jd.gui.util.parser.antlr.JavaParser;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.*;\n\n/**\n * Unsafe thread implementation of java file indexer.\n */\npublic class JavaFileIndexerProvider extends AbstractIndexerProvider {\n\n    static {\n        // Early class loading\n        ANTLRJavaParser.parse(new ANTLRInputStream(\"class EarlyLoading{}\"), new Listener(null));\n    }\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.java\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public void index(API api, Container.Entry entry, Indexes indexes) {\n        try (InputStream inputStream = entry.getInputStream()) {\n            Listener listener = new Listener(entry);\n            ANTLRJavaParser.parse(new ANTLRInputStream(inputStream), listener);\n\n            // Append sets to indexes\n            addToIndexes(indexes, \"typeDeclarations\", listener.getTypeDeclarationSet(), entry);\n            addToIndexes(indexes, \"constructorDeclarations\", listener.getConstructorDeclarationSet(), entry);\n            addToIndexes(indexes, \"methodDeclarations\", listener.getMethodDeclarationSet(), entry);\n            addToIndexes(indexes, \"fieldDeclarations\", listener.getFieldDeclarationSet(), entry);\n            addToIndexes(indexes, \"typeReferences\", listener.getTypeReferenceSet(), entry);\n            addToIndexes(indexes, \"constructorReferences\", listener.getConstructorReferenceSet(), entry);\n            addToIndexes(indexes, \"methodReferences\", listener.getMethodReferenceSet(), entry);\n            addToIndexes(indexes, \"fieldReferences\", listener.getFieldReferenceSet(), entry);\n            addToIndexes(indexes, \"strings\", listener.getStringSet(), entry);\n\n            // Populate map [super type name : [sub type name]]\n            Map<String, Collection> index = indexes.getIndex(\"subTypeNames\");\n\n            for (Map.Entry<String, HashSet<String>> e : listener.getSuperTypeNamesMap().entrySet()) {\n                String typeName = e.getKey();\n\n                for (String superTypeName : e.getValue()) {\n                    index.get(superTypeName).add(typeName);\n                }\n            }\n        } catch (IOException e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n    }\n\n    protected static class Listener extends AbstractJavaListener {\n\n        protected HashSet<String> typeDeclarationSet = new HashSet<>();\n        protected HashSet<String> constructorDeclarationSet = new HashSet<>();\n        protected HashSet<String> methodDeclarationSet = new HashSet<>();\n        protected HashSet<String> fieldDeclarationSet = new HashSet<>();\n        protected HashSet<String> typeReferenceSet = new HashSet<>();\n        protected HashSet<String> constructorReferenceSet = new HashSet<>();\n        protected HashSet<String> methodReferenceSet = new HashSet<>();\n        protected HashSet<String> fieldReferenceSet = new HashSet<>();\n        protected HashSet<String> stringSet = new HashSet<>();\n        protected HashMap<String, HashSet<String>> superTypeNamesMap = new HashMap<>();\n\n        protected StringBuilder sbTypeDeclaration = new StringBuilder();\n\n        public Listener(Container.Entry entry) {\n            super(entry);\n        }\n\n        public HashSet<String> getTypeDeclarationSet() { return typeDeclarationSet; }\n        public HashSet<String> getConstructorDeclarationSet() { return constructorDeclarationSet; }\n        public HashSet<String> getMethodDeclarationSet() { return methodDeclarationSet; }\n        public HashSet<String> getFieldDeclarationSet() { return fieldDeclarationSet; }\n        public HashSet<String> getTypeReferenceSet() { return typeReferenceSet; }\n        public HashSet<String> getConstructorReferenceSet() { return constructorReferenceSet; }\n        public HashSet<String> getMethodReferenceSet() { return methodReferenceSet; }\n        public HashSet<String> getFieldReferenceSet() { return fieldReferenceSet; }\n        public HashSet<String> getStringSet() { return stringSet; }\n        public HashMap<String, HashSet<String>> getSuperTypeNamesMap() { return superTypeNamesMap; }\n\n        // --- ANTLR Listener --- //\n\n        public void enterPackageDeclaration(JavaParser.PackageDeclarationContext ctx) {\n            super.enterPackageDeclaration(ctx);\n\n            if (! packageName.isEmpty()) {\n                sbTypeDeclaration.append(packageName).append('/');\n            }\n        }\n\n        public void enterClassDeclaration(JavaParser.ClassDeclarationContext ctx) { enterTypeDeclaration(ctx); }\n        public void exitClassDeclaration(JavaParser.ClassDeclarationContext ctx) { exitTypeDeclaration(); }\n\n        public void enterEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { enterTypeDeclaration(ctx); }\n        public void exitEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { exitTypeDeclaration(); }\n\n        public void enterInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { enterTypeDeclaration(ctx); }\n        public void exitInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { exitTypeDeclaration(); }\n\n        public void enterAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { enterTypeDeclaration(ctx); }\n        public void exitAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { exitTypeDeclaration(); }\n\n        protected void enterTypeDeclaration(ParserRuleContext ctx) {\n            // Add type declaration\n            TerminalNode identifier = ctx.getToken(JavaParser.Identifier, 0);\n\n            if (identifier != null) {\n                String typeName = identifier.getText();\n                int length = sbTypeDeclaration.length();\n\n                if ((length == 0) || (sbTypeDeclaration.charAt(length - 1) == '/')) {\n                    sbTypeDeclaration.append(typeName);\n                } else {\n                    sbTypeDeclaration.append('$').append(typeName);\n                }\n\n                String internalTypeName = sbTypeDeclaration.toString();\n                typeDeclarationSet.add(internalTypeName);\n                nameToInternalTypeName.put(typeName, internalTypeName);\n\n                HashSet<String> superInternalTypeNameSet = new HashSet<>();\n\n                // Add super type reference\n                JavaParser.TypeContext superType = ctx.getRuleContext(JavaParser.TypeContext.class, 0);\n                if (superType != null) {\n                    String superQualifiedTypeName = resolveInternalTypeName(superType.classOrInterfaceType().Identifier());\n\n                    if (superQualifiedTypeName.charAt(0) != '*')\n                        superInternalTypeNameSet.add(superQualifiedTypeName);\n                }\n\n                // Add implementation references\n                JavaParser.TypeListContext superInterfaces = ctx.getRuleContext(JavaParser.TypeListContext.class, 0);\n                if (superInterfaces != null) {\n                    for (JavaParser.TypeContext superInterface : superInterfaces.type()) {\n                        String superQualifiedInterfaceName = resolveInternalTypeName(superInterface.classOrInterfaceType().Identifier());\n\n                        if (superQualifiedInterfaceName.charAt(0) != '*')\n                            superInternalTypeNameSet.add(superQualifiedInterfaceName);\n                    }\n                }\n\n                if (!superInternalTypeNameSet.isEmpty()) {\n                    superTypeNamesMap.put(internalTypeName, superInternalTypeNameSet);\n                }\n            }\n        }\n\n        protected void exitTypeDeclaration() {\n            int index = sbTypeDeclaration.lastIndexOf(\"$\");\n\n            if (index == -1) {\n                index = sbTypeDeclaration.lastIndexOf(\"/\") + 1;\n            }\n\n            if (index == -1) {\n                sbTypeDeclaration.setLength(0);\n            } else {\n                sbTypeDeclaration.setLength(index);\n            }\n        }\n\n        public void enterType(JavaParser.TypeContext ctx) {\n            // Add type reference\n            JavaParser.ClassOrInterfaceTypeContext classOrInterfaceType = ctx.classOrInterfaceType();\n\n            if (classOrInterfaceType != null) {\n                String internalTypeName = resolveInternalTypeName(classOrInterfaceType.Identifier());\n\n                if (internalTypeName.charAt(0) != '*')\n                    typeReferenceSet.add(internalTypeName);\n            }\n        }\n\n        public void enterConstDeclaration(JavaParser.ConstDeclarationContext ctx) {\n            for (JavaParser.ConstantDeclaratorContext constantDeclaratorContext : ctx.constantDeclarator()) {\n                String name = constantDeclaratorContext.Identifier().getText();\n                fieldDeclarationSet.add(name);\n            }\n        }\n\n        public void enterFieldDeclaration(JavaParser.FieldDeclarationContext ctx) {\n            for (JavaParser.VariableDeclaratorContext declaration : ctx.variableDeclarators().variableDeclarator()) {\n                TerminalNode identifier = declaration.variableDeclaratorId().Identifier();\n\n                if (identifier != null) {\n                    String name = identifier.getText();\n                    fieldDeclarationSet.add(name);\n                }\n            }\n        }\n\n        public void enterMethodDeclaration(JavaParser.MethodDeclarationContext ctx) {\n            TerminalNode identifier = ctx.Identifier();\n\n            if (identifier != null) {\n                String name = identifier.getText();\n                methodDeclarationSet.add(name);\n            }\n        }\n\n        public void enterInterfaceMethodDeclaration(JavaParser.InterfaceMethodDeclarationContext ctx) {\n            TerminalNode identifier = ctx.Identifier();\n\n            if (identifier != null) {\n                String name = identifier.getText();\n                methodDeclarationSet.add(name);\n            }\n        }\n\n        public void enterConstructorDeclaration(JavaParser.ConstructorDeclarationContext ctx) {\n            String name = ctx.Identifier().getText();\n            constructorDeclarationSet.add(name);\n        }\n\n        public void enterCreatedName(JavaParser.CreatedNameContext ctx) {\n            String internalTypeName = resolveInternalTypeName(ctx.Identifier());\n\n            if ((internalTypeName != null) && (internalTypeName.charAt(0) != '*'))\n                constructorReferenceSet.add(internalTypeName);\n        }\n\n        public void enterExpression(JavaParser.ExpressionContext ctx) {\n            switch (ctx.getChildCount()) {\n                case 3:\n                    if (getToken(ctx.children, 1, JavaParser.DOT) != null) {\n                        // Search \"expression '.' Identifier\" : field\n                        TerminalNode identifier3 = getToken(ctx.children, 2, JavaParser.Identifier);\n\n                        if (identifier3 != null) {\n                            String fieldName = identifier3.getText();\n                            fieldReferenceSet.add(fieldName);\n                        }\n                    } else if (getToken(ctx.children, 1, JavaParser.LPAREN) != null) {\n                        // Search \"expression '(' ')'\" : method\n                        if (getToken(ctx.children, 2, JavaParser.RPAREN) != null) {\n                            TerminalNode identifier0 = getRightTerminalNode(ctx.children.get(0));\n\n                            if (identifier0 != null) {\n                                String methodName = identifier0.getText();\n                                methodReferenceSet.add(methodName);\n                            }\n                        }\n                    }\n                    break;\n                case 4:\n                    if (getToken(ctx.children, 1, JavaParser.LPAREN) != null) {\n                        // Search \"expression '(' expressionList ')'\" : method\n                        if (getToken(ctx.children, 3, JavaParser.RPAREN) != null) {\n                            JavaParser.ExpressionListContext expressionListContext = ctx.expressionList();\n\n                            if ((expressionListContext != null) && (expressionListContext == ctx.children.get(2))) {\n                                TerminalNode identifier0 = getRightTerminalNode(ctx.children.get(0));\n\n                                if (identifier0 != null) {\n                                    String methodName = identifier0.getText();\n                                    methodReferenceSet.add(methodName);\n                                }\n                            }\n                        }\n                    }\n                    break;\n            }\n        }\n\n        protected TerminalNode getToken(List<ParseTree> children, int i, int type) {\n            ParseTree pt = children.get(i);\n\n            if (pt instanceof TerminalNode) {\n                if (((TerminalNode)pt).getSymbol().getType() == type) {\n                    return (TerminalNode)pt;\n                }\n            }\n\n            return null;\n        }\n\n        protected TerminalNode getRightTerminalNode(ParseTree pt) {\n            if (pt instanceof ParserRuleContext) {\n                List<ParseTree> children = ((ParserRuleContext)pt).children;\n\n                if (children != null) {\n                    int size = children.size();\n\n                    if (size > 0) {\n                        ParseTree last = children.get(size - 1);\n\n                        if (last instanceof TerminalNode) {\n                            return (TerminalNode) last;\n                        } else {\n                            return getRightTerminalNode(last);\n                        }\n                    }\n                }\n            }\n\n            return null;\n        }\n\n        public void enterLiteral(JavaParser.LiteralContext ctx) {\n            TerminalNode stringLiteral = ctx.StringLiteral();\n            if (stringLiteral != null) {\n                stringSet.add(stringLiteral.getSymbol().getText());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/indexer/JavaModuleFileIndexerProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.indexer;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.spi.Indexer;\n\nimport java.util.Collection;\nimport java.util.Map;\n\npublic class JavaModuleFileIndexerProvider extends AbstractIndexerProvider {\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.jmod\"); }\n\n    @Override\n    public void index(API api, Container.Entry entry, Indexes indexes) {\n        for (Container.Entry e : entry.getChildren()) {\n            if (e.isDirectory() && e.getPath().equals(\"classes\")) {\n                Map<String, Collection> packageDeclarationIndex = indexes.getIndex(\"packageDeclarations\");\n\n                // Index module-info, packages and CLASS files\n                index(api, e, indexes, packageDeclarationIndex);\n                break;\n            }\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected static void index(API api, Container.Entry entry, Indexes indexes, Map<String, Collection> packageDeclarationIndex) {\n        for (Container.Entry e : entry.getChildren()) {\n            if (e.isDirectory()) {\n                String path = e.getPath();\n\n                if (!path.startsWith(\"classes/META-INF\")) {\n                    packageDeclarationIndex.get(path.substring(8)).add(e); // 8 = \"classes/\".length()\n                }\n\n                index(api, e, indexes, packageDeclarationIndex);\n            } else {\n                Indexer indexer = api.getIndexer(e);\n\n                if (indexer != null) {\n                    indexer.index(api, e, indexes);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/indexer/JavaModuleInfoFileIndexerProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.indexer;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.objectweb.asm.*;\n\nimport java.io.InputStream;\nimport java.util.HashSet;\n\nimport static org.objectweb.asm.ClassReader.*;\n\n/**\n * Unsafe thread implementation of class file indexer.\n */\npublic class JavaModuleInfoFileIndexerProvider extends AbstractIndexerProvider {\n    protected HashSet<String> javaModuleDeclarationSet = new HashSet<>();\n    protected HashSet<String> javaModuleReferenceSet = new HashSet<>();\n    protected HashSet<String> typeReferenceSet = new HashSet<>();\n\n    protected ClassIndexer classIndexer = new ClassIndexer();\n\n    @Override public String[] getSelectors() { return appendSelectors(\"jmod:file:classes/module-info.class\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public void index(API api, Container.Entry entry, Indexes indexes) {\n        // Cleaning sets...\n        javaModuleDeclarationSet.clear();\n        javaModuleReferenceSet.clear();\n        typeReferenceSet.clear();\n\n        try (InputStream inputStream = entry.getInputStream()) {\n            // Index field, method, interfaces & super type\n            ClassReader classReader = new ClassReader(inputStream);\n            classReader.accept(classIndexer, SKIP_CODE|SKIP_DEBUG|SKIP_FRAMES);\n\n            // Append sets to indexes\n            addToIndexes(indexes, \"javaModuleDeclarations\", javaModuleDeclarationSet, entry);\n            addToIndexes(indexes, \"javaModuleReferences\", javaModuleReferenceSet, entry);\n            addToIndexes(indexes, \"typeReferences\", typeReferenceSet, entry);\n        } catch (Exception e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n    }\n\n    protected class ClassIndexer extends ClassVisitor {\n        protected ModuleIndexer moduleIndexer = new ModuleIndexer();\n\n        public ClassIndexer() { super(Opcodes.ASM7); }\n\n        @Override\n        public ModuleVisitor visitModule(String moduleName, int moduleFlags, String moduleVersion) {\n            javaModuleDeclarationSet.add(moduleName);\n            return moduleIndexer;\n        }\n    }\n\n    protected class ModuleIndexer extends ModuleVisitor {\n        public ModuleIndexer() { super(Opcodes.ASM7); }\n\n        @Override public void visitMainClass(final String mainClass) { typeReferenceSet.add(mainClass); }\n        @Override public void visitRequire(final String module, final int access, final String version) { javaModuleReferenceSet.add(module); }\n        @Override public void visitUse(final String service) { typeReferenceSet.add(service); }\n\n        @Override\n        public void visitExport(final String packaze, final int access, final String... modules) {\n            if (modules != null) {\n                for (String module : modules) {\n                    javaModuleReferenceSet.add(module);\n                }\n            }\n        }\n\n        @Override\n        public void visitOpen(final String packaze, final int access, final String... modules) {\n            if (modules != null) {\n                for (String module : modules) {\n                    javaModuleReferenceSet.add(module);\n                }\n            }\n        }\n\n        @Override\n        public void visitProvide(final String service, final String... providers) {\n            typeReferenceSet.add(service);\n\n            if (providers != null) {\n                for (String provider : providers) {\n                    typeReferenceSet.add(provider);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/indexer/MetainfServiceFileIndexerProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.indexer;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.regex.Pattern;\n\npublic class MetainfServiceFileIndexerProvider extends AbstractIndexerProvider {\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*\"); }\n\n    @Override public Pattern getPathPattern() { return (externalPathPattern != null) ? externalPathPattern : Pattern.compile(\"META-INF\\\\/services\\\\/[^\\\\/]+\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public void index(API api, Container.Entry entry, Indexes indexes) {\n        Map<String, Collection> index = indexes.getIndex(\"typeReferences\");\n\n        try (BufferedReader br = new BufferedReader(new InputStreamReader(entry.getInputStream()))) {\n            String line;\n\n            while ((line = br.readLine()) != null) {\n                String trim = line.trim();\n\n                if (!trim.isEmpty() && (trim.charAt(0) != '#')) {\n                    String internalTypeName = trim.replace(\".\", \"/\");\n\n                    index.get(internalTypeName).add(entry);\n                }\n            }\n        } catch (IOException e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/indexer/TextFileIndexerProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.indexer;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.util.io.TextReader;\n\npublic class TextFileIndexerProvider extends AbstractIndexerProvider {\n\n    @Override public String[] getSelectors() {\n        return appendSelectors(\n                \"*:file:*.txt\", \"*:file:*.html\", \"*:file:*.xhtml\", \"*:file:*.js\", \"*:file:*.jsp\", \"*:file:*.jspf\",\n                \"*:file:*.xml\", \"*:file:*.xsl\", \"*:file:*.xslt\", \"*:file:*.xsd\", \"*:file:*.properties\", \"*:file:*.sql\",\n                \"*:file:*.yaml\", \"*:file:*.yml\", \"*:file:*.json\");\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public void index(API api, Container.Entry entry, Indexes indexes) {\n        indexes.getIndex(\"strings\").get(TextReader.getText(entry.getInputStream())).add(entry);\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/indexer/WebXmlFileIndexerProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.indexer;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.util.io.TextReader;\nimport org.jd.gui.util.xml.AbstractXmlPathFinder;\n\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Map;\n\npublic class WebXmlFileIndexerProvider extends XmlBasedFileIndexerProvider {\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:WEB-INF/web.xml\"); }\n\n    @Override\n    public void index(API api, Container.Entry entry, Indexes indexes) {\n        super.index(api, entry, indexes);\n\n        new WebXmlPathFinder(entry, indexes).find(TextReader.getText(entry.getInputStream()));\n    }\n\n    protected static class WebXmlPathFinder extends AbstractXmlPathFinder {\n        Container.Entry entry;\n        Map<String, Collection> index;\n\n        public WebXmlPathFinder(Container.Entry entry, Indexes indexes) {\n            super(Arrays.asList(\n                \"web-app/filter/filter-class\",\n                \"web-app/listener/listener-class\",\n                \"web-app/servlet/servlet-class\"\n            ));\n            this.entry = entry;\n            this.index = indexes.getIndex(\"typeReferences\");\n        }\n\n        @Override\n        @SuppressWarnings(\"unchecked\")\n        public void handle(String path, String text, int position) {\n            index.get(text.replace(\".\", \"/\")).add(entry);\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/indexer/XmlBasedFileIndexerProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.indexer;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport javax.xml.stream.XMLInputFactory;\nimport javax.xml.stream.XMLStreamConstants;\nimport javax.xml.stream.XMLStreamException;\nimport javax.xml.stream.XMLStreamReader;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.Map;\n\npublic class XmlBasedFileIndexerProvider extends AbstractIndexerProvider {\n    protected XMLInputFactory factory;\n\n    public XmlBasedFileIndexerProvider() {\n        factory = XMLInputFactory.newInstance();\n        factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);\n    }\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.xsl\", \"*:file:*.xslt\", \"*:file:*.xsd\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public void index(API api, Container.Entry entry, Indexes indexes) {\n        HashSet<String> stringSet = new HashSet<>();\n        XMLStreamReader reader = null;\n\n        try {\n            reader = factory.createXMLStreamReader(entry.getInputStream());\n\n            stringSet.add(reader.getVersion());\n            stringSet.add(reader.getEncoding());\n            stringSet.add(reader.getCharacterEncodingScheme());\n\n            while (reader.hasNext()) {\n                switch (reader.next()) {\n                    case XMLStreamConstants.START_ELEMENT:\n                        stringSet.add(reader.getLocalName());\n                        for (int i = reader.getAttributeCount() - 1; i >= 0; i--) {\n                            stringSet.add(reader.getAttributeLocalName(i));\n                            stringSet.add(reader.getAttributeValue(i));\n                        }\n                        for (int i = reader.getNamespaceCount() - 1; i >= 0; i--) {\n                            stringSet.add(reader.getNamespacePrefix(i));\n                            stringSet.add(reader.getNamespaceURI(i));\n                        }\n                        break;\n                    case XMLStreamConstants.PROCESSING_INSTRUCTION:\n                        stringSet.add(reader.getPITarget());\n                        stringSet.add(reader.getPIData());\n                        break;\n                    case XMLStreamConstants.START_DOCUMENT:\n                        stringSet.add(reader.getVersion());\n                        stringSet.add(reader.getEncoding());\n                        stringSet.add(reader.getCharacterEncodingScheme());\n                        break;\n                    case XMLStreamConstants.ENTITY_REFERENCE:\n                        stringSet.add(reader.getLocalName());\n                        stringSet.add(reader.getText());\n                        break;\n                    case XMLStreamConstants.ATTRIBUTE:\n                        stringSet.add(reader.getPrefix());\n                        stringSet.add(reader.getNamespaceURI());\n                        stringSet.add(reader.getLocalName());\n                        stringSet.add(reader.getText());\n                        break;\n                    case XMLStreamConstants.COMMENT:\n                    case XMLStreamConstants.DTD:\n                    case XMLStreamConstants.CDATA:\n                    case XMLStreamConstants.CHARACTERS:\n                        stringSet.add(reader.getText().trim());\n                        break;\n                    case XMLStreamConstants.NAMESPACE:\n                        for (int i = reader.getNamespaceCount() - 1; i >= 0; i--) {\n                            stringSet.add(reader.getNamespacePrefix(i));\n                            stringSet.add(reader.getNamespaceURI(i));\n                        }\n                        break;\n                }\n            }\n        } catch (Exception e) {\n            assert ExceptionUtil.printStackTrace(e);\n        } finally {\n            if (reader != null) {\n                try {\n                    reader.close();\n                } catch (XMLStreamException e) {\n                    assert ExceptionUtil.printStackTrace(e);\n                }\n            }\n        }\n\n        Map<String, Collection> stringIndex = indexes.getIndex(\"strings\");\n\n        for (String string : stringSet) {\n            if ((string != null) && !string.isEmpty()) {\n                stringIndex.get(string).add(entry);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/indexer/XmlFileIndexerProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.indexer;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport javax.xml.stream.XMLInputFactory;\nimport javax.xml.stream.XMLStreamConstants;\nimport javax.xml.stream.XMLStreamException;\nimport javax.xml.stream.XMLStreamReader;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.Map;\n\npublic class XmlFileIndexerProvider extends AbstractIndexerProvider {\n    protected XMLInputFactory factory;\n\n    public XmlFileIndexerProvider() {\n        factory = XMLInputFactory.newInstance();\n        factory.setProperty(XMLInputFactory.SUPPORT_DTD, false);\n    }\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.xml\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public void index(API api, Container.Entry entry, Indexes indexes) {\n        HashSet<String> stringSet = new HashSet<>();\n        HashSet<String> typeReferenceSet = new HashSet<>();\n        XMLStreamReader reader = null;\n\n        try {\n            reader = factory.createXMLStreamReader(entry.getInputStream());\n\n            stringSet.add(reader.getVersion());\n            stringSet.add(reader.getEncoding());\n            stringSet.add(reader.getCharacterEncodingScheme());\n\n            while (reader.hasNext()) {\n                switch (reader.next()) {\n                    case XMLStreamConstants.START_ELEMENT:\n                        boolean beanFlag = reader.getLocalName().equals(\"bean\");\n\n                        stringSet.add(reader.getLocalName());\n                        for (int i = reader.getAttributeCount() - 1; i >= 0; i--) {\n                            String attributeName = reader.getAttributeLocalName(i);\n\n                            stringSet.add(attributeName);\n\n                            if (beanFlag && attributeName.equals(\"class\")) {\n                                // String bean reference\n                                typeReferenceSet.add(reader.getAttributeValue(i).replace(\".\", \"/\"));\n                            } else {\n                                stringSet.add(reader.getAttributeValue(i));\n                            }\n                        }\n                        for (int i = reader.getNamespaceCount() - 1; i >= 0; i--) {\n                            stringSet.add(reader.getNamespacePrefix(i));\n                            stringSet.add(reader.getNamespaceURI(i));\n                        }\n                        break;\n                    case XMLStreamConstants.PROCESSING_INSTRUCTION:\n                        stringSet.add(reader.getPITarget());\n                        stringSet.add(reader.getPIData());\n                        break;\n                    case XMLStreamConstants.START_DOCUMENT:\n                        stringSet.add(reader.getVersion());\n                        stringSet.add(reader.getEncoding());\n                        stringSet.add(reader.getCharacterEncodingScheme());\n                        break;\n                    case XMLStreamConstants.ENTITY_REFERENCE:\n                        stringSet.add(reader.getLocalName());\n                        stringSet.add(reader.getText());\n                        break;\n                    case XMLStreamConstants.ATTRIBUTE:\n                        stringSet.add(reader.getPrefix());\n                        stringSet.add(reader.getNamespaceURI());\n                        stringSet.add(reader.getLocalName());\n                        stringSet.add(reader.getText());\n                        break;\n                    case XMLStreamConstants.COMMENT:\n                    case XMLStreamConstants.DTD:\n                    case XMLStreamConstants.CDATA:\n                    case XMLStreamConstants.CHARACTERS:\n                        stringSet.add(reader.getText().trim());\n                        break;\n                    case XMLStreamConstants.NAMESPACE:\n                        for (int i = reader.getNamespaceCount() - 1; i >= 0; i--) {\n                            stringSet.add(reader.getNamespacePrefix(i));\n                            stringSet.add(reader.getNamespaceURI(i));\n                        }\n                        break;\n                }\n            }\n        } catch (Exception e) {\n            assert ExceptionUtil.printStackTrace(e);\n        } finally {\n            if (reader != null) {\n                try {\n                    reader.close();\n                } catch (XMLStreamException e) {\n                    assert ExceptionUtil.printStackTrace(e);\n                }\n            }\n        }\n\n        Map<String, Collection> stringIndex = indexes.getIndex(\"strings\");\n        Map<String, Collection> typeReferenceIndex = indexes.getIndex(\"typeReferences\");\n\n        for (String string : stringSet) {\n            if ((string != null) && !string.isEmpty()) {\n                stringIndex.get(string).add(entry);\n            }\n        }\n\n        for (String ref : typeReferenceSet) {\n            if ((ref != null) && !ref.isEmpty()) {\n                typeReferenceIndex.get(ref).add(entry);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/indexer/ZipFileIndexerProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.indexer;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.spi.Indexer;\n\npublic class ZipFileIndexerProvider extends AbstractIndexerProvider {\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.zip\", \"*:file:*.jar\", \"*:file:*.war\", \"*:file:*.ear\", \"*:file:*.aar\", \"*:file:*.kar\"); }\n\n    @Override\n    public void index(API api, Container.Entry entry, Indexes indexes) {\n        for (Container.Entry e : entry.getChildren()) {\n            if (e.isDirectory()) {\n                index(api, e, indexes);\n            } else {\n                Indexer indexer = api.getIndexer(e);\n\n                if (indexer != null) {\n                    indexer.index(api, e, indexes);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/pastehandler/LogPasteHandler.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.pastehandler;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.spi.PasteHandler;\nimport org.jd.gui.view.component.LogPage;\n\nimport java.net.URI;\n\npublic class LogPasteHandler implements PasteHandler {\n    protected static int counter = 0;\n\n    public boolean accept(Object obj) { return obj instanceof String; }\n\n    public void paste(API api, Object obj) {\n        String title = \"clipboard-\" + (++counter) + \".log\";\n        URI uri = URI.create(\"memory://\" + title);\n        String content = (obj == null) ? null : obj.toString();\n        api.addPanel(title, null, null, new LogPage(api, uri, content));\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/preferencespanel/ClassFileDecompilerPreferencesProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.preferencespanel;\n\nimport org.jd.gui.spi.PreferencesPanel;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.util.Map;\n\npublic class ClassFileDecompilerPreferencesProvider extends JPanel implements PreferencesPanel {\n    protected static final String ESCAPE_UNICODE_CHARACTERS = \"ClassFileDecompilerPreferences.escapeUnicodeCharacters\";\n    protected static final String REALIGN_LINE_NUMBERS = \"ClassFileDecompilerPreferences.realignLineNumbers\";\n\n    protected PreferencesPanel.PreferencesPanelChangeListener listener = null;\n    protected JCheckBox escapeUnicodeCharactersCheckBox;\n    protected JCheckBox realignLineNumbersCheckBox;\n\n    public ClassFileDecompilerPreferencesProvider() {\n        super(new GridLayout(0,1));\n\n        escapeUnicodeCharactersCheckBox = new JCheckBox(\"Escape unicode characters\");\n        realignLineNumbersCheckBox = new JCheckBox(\"Realign line numbers\");\n\n        add(escapeUnicodeCharactersCheckBox);\n        add(realignLineNumbersCheckBox);\n    }\n\n    // --- PreferencesPanel --- //\n    @Override public String getPreferencesGroupTitle() { return \"Decompiler\"; }\n    @Override public String getPreferencesPanelTitle() { return \"Class file\"; }\n    @Override public JComponent getPanel() { return this; }\n\n    @Override public void init(Color errorBackgroundColor) {}\n\n    @Override public boolean isActivated() { return true; }\n\n    @Override\n    public void loadPreferences(Map<String, String> preferences) {\n        escapeUnicodeCharactersCheckBox.setSelected(\"true\".equals(preferences.get(ESCAPE_UNICODE_CHARACTERS)));\n        realignLineNumbersCheckBox.setSelected(\"true\".equals(preferences.get(REALIGN_LINE_NUMBERS)));\n    }\n\n    @Override\n    public void savePreferences(Map<String, String> preferences) {\n        preferences.put(ESCAPE_UNICODE_CHARACTERS, Boolean.toString(escapeUnicodeCharactersCheckBox.isSelected()));\n        preferences.put(REALIGN_LINE_NUMBERS, Boolean.toString(realignLineNumbersCheckBox.isSelected()));\n    }\n\n    @Override public boolean arePreferencesValid() { return true; }\n\n    @Override public void addPreferencesChangeListener(PreferencesPanel.PreferencesPanelChangeListener listener) {}\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/preferencespanel/ClassFileSaverPreferencesProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.preferencespanel;\n\nimport org.jd.gui.spi.PreferencesPanel;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.util.Map;\n\npublic class ClassFileSaverPreferencesProvider extends JPanel implements PreferencesPanel {\n    protected static final String WRITE_LINE_NUMBERS = \"ClassFileSaverPreferences.writeLineNumbers\";\n    protected static final String WRITE_METADATA = \"ClassFileSaverPreferences.writeMetadata\";\n\n    protected JCheckBox writeLineNumbersCheckBox;\n    protected JCheckBox writeMetadataCheckBox;\n\n    public ClassFileSaverPreferencesProvider() {\n        super(new GridLayout(0,1));\n\n        writeLineNumbersCheckBox = new JCheckBox(\"Write original line numbers\");\n        writeMetadataCheckBox = new JCheckBox(\"Write metadata\");\n\n        add(writeLineNumbersCheckBox);\n        add(writeMetadataCheckBox);\n    }\n\n    // --- PreferencesPanel --- //\n    @Override public String getPreferencesGroupTitle() { return \"Source Saver\"; }\n    @Override public String getPreferencesPanelTitle() { return \"Class file\"; }\n    @Override public JComponent getPanel() { return this; }\n\n    @Override public void init(Color errorBackgroundColor) {}\n\n    @Override public boolean isActivated() { return true; }\n\n    @Override\n    public void loadPreferences(Map<String, String> preferences) {\n        writeLineNumbersCheckBox.setSelected(!\"false\".equals(preferences.get(WRITE_LINE_NUMBERS)));\n        writeMetadataCheckBox.setSelected(!\"false\".equals(preferences.get(WRITE_METADATA)));\n    }\n\n    @Override\n    public void savePreferences(Map<String, String> preferences) {\n        preferences.put(WRITE_LINE_NUMBERS, Boolean.toString(writeLineNumbersCheckBox.isSelected()));\n        preferences.put(WRITE_METADATA, Boolean.toString(writeMetadataCheckBox.isSelected()));\n    }\n\n    @Override public boolean arePreferencesValid() { return true; }\n\n    @Override public void addPreferencesChangeListener(PreferencesPanel.PreferencesPanelChangeListener listener) {}\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/preferencespanel/DirectoryIndexerPreferencesProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.preferencespanel;\n\nimport org.jd.gui.spi.PreferencesPanel;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport javax.swing.*;\nimport javax.swing.event.DocumentEvent;\nimport javax.swing.event.DocumentListener;\nimport java.awt.*;\nimport java.util.Map;\n\npublic class DirectoryIndexerPreferencesProvider extends JPanel implements PreferencesPanel, DocumentListener {\n    protected static final int MAX_VALUE = 30;\n    protected static final String MAXIMUM_DEPTH_KEY = \"DirectoryIndexerPreferences.maximumDepth\";\n\n    protected PreferencesPanel.PreferencesPanelChangeListener listener = null;\n    protected JTextField maximumDepthTextField;\n    protected Color errorBackgroundColor = Color.RED;\n    protected Color defaultBackgroundColor;\n\n    public DirectoryIndexerPreferencesProvider() {\n        super(new BorderLayout());\n\n        add(new JLabel(\"Maximum depth (1..\" + MAX_VALUE + \"): \"), BorderLayout.WEST);\n\n        maximumDepthTextField = new JTextField();\n        maximumDepthTextField.getDocument().addDocumentListener(this);\n        add(maximumDepthTextField, BorderLayout.CENTER);\n\n        defaultBackgroundColor = maximumDepthTextField.getBackground();\n    }\n\n    // --- PreferencesPanel --- //\n    @Override public String getPreferencesGroupTitle() { return \"Indexer\"; }\n    @Override public String getPreferencesPanelTitle() { return \"Directory exploration\"; }\n    @Override public JComponent getPanel() { return this; }\n\n    @Override public void init(Color errorBackgroundColor) {\n        this.errorBackgroundColor = errorBackgroundColor;\n    }\n\n    @Override public boolean isActivated() { return true; }\n\n    @Override public void loadPreferences(Map<String, String> preferences) {\n        String preference = preferences.get(MAXIMUM_DEPTH_KEY);\n\n        maximumDepthTextField.setText((preference != null) ? preference : \"15\");\n        maximumDepthTextField.setCaretPosition(maximumDepthTextField.getText().length());\n    }\n\n    @Override\n    public void savePreferences(Map<String, String> preferences) {\n        preferences.put(MAXIMUM_DEPTH_KEY, maximumDepthTextField.getText());\n    }\n\n    @Override\n    public boolean arePreferencesValid() {\n        try {\n            int i = Integer.valueOf(maximumDepthTextField.getText());\n            return (i > 0) && (i <= MAX_VALUE);\n        } catch (NumberFormatException e) {\n            assert ExceptionUtil.printStackTrace(e);\n            return false;\n        }\n    }\n\n    @Override\n    public void addPreferencesChangeListener(PreferencesPanel.PreferencesPanelChangeListener listener) {\n        this.listener = listener;\n    }\n\n    // --- DocumentListener --- //\n    @Override public void insertUpdate(DocumentEvent e) { onTextChange(); }\n    @Override public void removeUpdate(DocumentEvent e) { onTextChange(); }\n    @Override public void changedUpdate(DocumentEvent e) { onTextChange(); }\n\n    public void onTextChange() {\n        maximumDepthTextField.setBackground(arePreferencesValid() ? defaultBackgroundColor : errorBackgroundColor);\n\n        if (listener != null) {\n            listener.preferencesPanelChanged(this);\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/preferencespanel/MavenOrgSourceLoaderPreferencesProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.preferencespanel;\n\nimport org.jd.gui.spi.PreferencesPanel;\n\nimport javax.swing.*;\nimport javax.swing.event.DocumentEvent;\nimport javax.swing.event.DocumentListener;\nimport java.awt.*;\nimport java.awt.event.ActionEvent;\nimport java.awt.event.ActionListener;\nimport java.util.Map;\nimport java.util.regex.Pattern;\n\npublic class MavenOrgSourceLoaderPreferencesProvider extends JPanel implements PreferencesPanel, DocumentListener, ActionListener {\n    public static final String ACTIVATED = \"MavenOrgSourceLoaderPreferencesProvider.activated\";\n    public static final String FILTERS = \"MavenOrgSourceLoaderPreferencesProvider.filters\";\n\n    public static final String DEFAULT_FILTERS_VALUE =\n            \"+org +com.google +com.springsource +com.sun -com +java +javax +sun +sunw \" +\n            \"+spring +springframework +springmodules +tomcat +maven +edu\";\n\n    protected static final Pattern CONTROL_PATTERN = Pattern.compile(\"([+-][a-zA-Z_0-9$_.]+(\\\\s+[+-][a-zA-Z_0-9$_.]+)*)?\\\\s*\");\n\n    protected JCheckBox enableCheckBox;\n    protected JTextArea filtersTextArea;\n    protected JButton resetButton;\n    protected Color errorBackgroundColor = Color.RED;\n    protected Color defaultBackgroundColor;\n\n    protected PreferencesPanel.PreferencesPanelChangeListener listener;\n\n    public MavenOrgSourceLoaderPreferencesProvider() {\n        super(new BorderLayout());\n\n        enableCheckBox = new JCheckBox(\"Search source code on maven.org for:\");\n        enableCheckBox.addActionListener(this);\n\n        filtersTextArea = new JTextArea();\n        filtersTextArea.setFont(getFont());\n        filtersTextArea.setLineWrap(true);\n        filtersTextArea.getDocument().addDocumentListener(this);\n        defaultBackgroundColor = filtersTextArea.getBackground();\n\n        JComponent spacer = new JComponent() {};\n        JScrollPane scrollPane = new JScrollPane(filtersTextArea);\n\n        String osName = System.getProperty(\"os.name\").toLowerCase();\n\n        if (osName.contains(\"windows\")) {\n            spacer.setPreferredSize(new Dimension(22, -1));\n            scrollPane.setPreferredSize(new Dimension(-1, 50));\n        } else if (osName.contains(\"mac os\")) {\n            spacer.setPreferredSize(new Dimension(28, -1));\n            scrollPane.setPreferredSize(new Dimension(-1, 56));\n        } else {\n            spacer.setPreferredSize(new Dimension(22, -1));\n            scrollPane.setPreferredSize(new Dimension(-1, 56));\n        }\n\n        resetButton = new JButton(\"Reset\");\n        resetButton.addActionListener(this);\n\n        JPanel southPanel = new JPanel(new BorderLayout());\n        southPanel.add(resetButton, BorderLayout.EAST);\n\n        add(enableCheckBox, BorderLayout.NORTH);\n        add(spacer, BorderLayout.WEST);\n        add(scrollPane, BorderLayout.CENTER);\n        add(southPanel, BorderLayout.SOUTH);\n    }\n\n    // --- PreferencesPanel --- //\n    @Override public String getPreferencesGroupTitle() { return \"Source loader\"; }\n    @Override public String getPreferencesPanelTitle() { return \"maven.org\"; }\n    @Override public JComponent getPanel() { return this; }\n\n    @Override\n    public void init(Color errorBackgroundColor) {\n        this.errorBackgroundColor = errorBackgroundColor;\n    }\n\n    @Override public boolean isActivated() { return true; }\n\n    @Override\n    public void loadPreferences(Map<String, String> preferences) {\n        boolean enabled = !\"false\".equals(preferences.get(ACTIVATED));\n\n        enableCheckBox.setSelected(enabled);\n        filtersTextArea.setEnabled(enabled);\n        resetButton.setEnabled(enabled);\n\n        String filters = preferences.get(FILTERS);\n\n        filtersTextArea.setText((filters == null) || filters.isEmpty() ? DEFAULT_FILTERS_VALUE : filters);\n    }\n\n    @Override\n    public void savePreferences(Map<String, String> preferences) {\n        preferences.put(ACTIVATED, Boolean.toString(enableCheckBox.isSelected()));\n        preferences.put(FILTERS, filtersTextArea.getText().trim());\n    }\n\n    @Override public boolean arePreferencesValid() {\n        return CONTROL_PATTERN.matcher(filtersTextArea.getText()).matches();\n    }\n\n    @Override public void addPreferencesChangeListener(PreferencesPanelChangeListener listener) {\n        this.listener = listener;\n    }\n\n\n    // --- DocumentListener --- //\n    @Override public void insertUpdate(DocumentEvent e) { onTextChange(); }\n    @Override public void removeUpdate(DocumentEvent e) { onTextChange(); }\n    @Override public void changedUpdate(DocumentEvent e) { onTextChange(); }\n\n    protected void onTextChange() {\n        filtersTextArea.setBackground(arePreferencesValid() ? defaultBackgroundColor : errorBackgroundColor);\n\n        if (listener != null) {\n            listener.preferencesPanelChanged(this);\n        }\n    }\n\n    // --- ActionListener --- //\n    @Override\n    public void actionPerformed(ActionEvent e) {\n        if (e.getSource() == enableCheckBox) {\n            boolean enabled = enableCheckBox.isSelected();\n            filtersTextArea.setEnabled(enabled);\n            resetButton.setEnabled(enabled);\n        } else {\n            // Reset button\n            filtersTextArea.setText(DEFAULT_FILTERS_VALUE);\n            filtersTextArea.requestFocus();\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/preferencespanel/ViewerPreferencesProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.preferencespanel;\n\nimport org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;\nimport org.fife.ui.rsyntaxtextarea.Theme;\nimport org.jd.gui.spi.PreferencesPanel;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport javax.swing.*;\nimport javax.swing.event.DocumentEvent;\nimport javax.swing.event.DocumentListener;\nimport java.awt.*;\nimport java.io.IOException;\nimport java.util.Map;\n\npublic class ViewerPreferencesProvider extends JPanel implements PreferencesPanel, DocumentListener {\n    protected static final int MIN_VALUE = 2;\n    protected static final int MAX_VALUE = 40;\n    protected static final String FONT_SIZE_KEY = \"ViewerPreferences.fontSize\";\n\n    protected PreferencesPanel.PreferencesPanelChangeListener listener = null;\n    protected JTextField fontSizeTextField;\n    protected Color errorBackgroundColor = Color.RED;\n    protected Color defaultBackgroundColor;\n\n    public ViewerPreferencesProvider() {\n        super(new BorderLayout());\n\n        add(new JLabel(\"Font size (\" + MIN_VALUE + \"..\" + MAX_VALUE + \"): \"), BorderLayout.WEST);\n\n        fontSizeTextField = new JTextField();\n        fontSizeTextField.getDocument().addDocumentListener(this);\n        add(fontSizeTextField, BorderLayout.CENTER);\n\n        defaultBackgroundColor = fontSizeTextField.getBackground();\n    }\n\n    // --- PreferencesPanel --- //\n    @Override public String getPreferencesGroupTitle() { return \"Viewer\"; }\n    @Override public String getPreferencesPanelTitle() { return \"Appearance\"; }\n    @Override public JComponent getPanel() { return this; }\n\n    @Override public void init(Color errorBackgroundColor) {\n        this.errorBackgroundColor = errorBackgroundColor;\n    }\n\n    @Override public boolean isActivated() { return true; }\n\n    @Override\n    public void loadPreferences(Map<String, String> preferences) {\n        String fontSize = preferences.get(FONT_SIZE_KEY);\n\n        if (fontSize == null) {\n            // Search default value for the current platform\n            RSyntaxTextArea textArea = new RSyntaxTextArea();\n\n            try {\n                Theme theme = Theme.load(getClass().getClassLoader().getResourceAsStream(\"rsyntaxtextarea/themes/eclipse.xml\"));\n                theme.apply(textArea);\n            } catch (IOException e) {\n                assert ExceptionUtil.printStackTrace(e);\n            }\n\n            fontSize = String.valueOf(textArea.getFont().getSize());\n        }\n\n        fontSizeTextField.setText(fontSize);\n        fontSizeTextField.setCaretPosition(fontSizeTextField.getText().length());\n    }\n\n    @Override\n    public void savePreferences(Map<String, String> preferences) {\n        preferences.put(FONT_SIZE_KEY, fontSizeTextField.getText());\n    }\n\n    @Override\n    public boolean arePreferencesValid() {\n        try {\n            int i = Integer.valueOf(fontSizeTextField.getText());\n            return (i >= MIN_VALUE) && (i <= MAX_VALUE);\n        } catch (NumberFormatException e) {\n            assert ExceptionUtil.printStackTrace(e);\n            return false;\n        }\n    }\n\n    @Override\n    public void addPreferencesChangeListener(PreferencesPanel.PreferencesPanelChangeListener listener) {\n        this.listener = listener;\n    }\n\n    // --- DocumentListener --- //\n    @Override public void insertUpdate(DocumentEvent e) { onTextChange(); }\n    @Override public void removeUpdate(DocumentEvent e) { onTextChange(); }\n    @Override public void changedUpdate(DocumentEvent e) { onTextChange(); }\n\n    public void onTextChange() {\n        fontSizeTextField.setBackground(arePreferencesValid() ? defaultBackgroundColor : errorBackgroundColor);\n\n        if (listener != null) {\n            listener.preferencesPanelChanged(this);\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/sourceloader/MavenOrgSourceLoaderProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.sourceloader;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.service.preferencespanel.MavenOrgSourceLoaderPreferencesProvider;\nimport org.jd.gui.spi.SourceLoader;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport javax.xml.stream.XMLInputFactory;\nimport javax.xml.stream.XMLStreamConstants;\nimport javax.xml.stream.XMLStreamReader;\nimport java.io.*;\nimport java.net.URL;\nimport java.security.DigestInputStream;\nimport java.security.MessageDigest;\nimport java.util.*;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipInputStream;\n\npublic class MavenOrgSourceLoaderProvider implements SourceLoader {\n    protected static final String MAVENORG_SEARCH_URL_PREFIX = \"https://search.maven.org/solrsearch/select?q=1:%22\";\n    protected static final String MAVENORG_SEARCH_URL_SUFFIX = \"%22&rows=20&wt=xml\";\n\n    protected static final String MAVENORG_LOAD_URL_PREFIX = \"https://search.maven.org/classic/remotecontent?filepath=\";\n    protected static final String MAVENORG_LOAD_URL_SUFFIX = \"-sources.jar\";\n\n    protected HashSet<Container.Entry> failed = new HashSet<>();\n    protected HashMap<Container.Entry, File> cache = new HashMap<>();\n\n    @Override\n    public String getSource(API api, Container.Entry entry) {\n        if (isActivated(api)) {\n            String filters = api.getPreferences().get(MavenOrgSourceLoaderPreferencesProvider.FILTERS);\n\n            if ((filters == null) || filters.isEmpty()) {\n                filters = MavenOrgSourceLoaderPreferencesProvider.DEFAULT_FILTERS_VALUE;\n            }\n\n            if (accepted(filters, entry.getPath())) {\n                return searchSource(entry, cache.get(entry.getContainer().getRoot().getParent()));\n            }\n        }\n\n        return null;\n    }\n\n    @Override\n    public String loadSource(API api, Container.Entry entry) {\n        if (isActivated(api)) {\n            String filters = api.getPreferences().get(MavenOrgSourceLoaderPreferencesProvider.FILTERS);\n\n            if ((filters == null) || filters.isEmpty()) {\n                filters = MavenOrgSourceLoaderPreferencesProvider.DEFAULT_FILTERS_VALUE;\n            }\n\n            if (accepted(filters, entry.getPath())) {\n                return searchSource(entry, downloadSourceJarFile(entry.getContainer().getRoot().getParent()));\n            }\n        }\n\n        return null;\n    }\n\n    @Override\n    public File loadSourceFile(API api, Container.Entry entry) {\n        return isActivated(api) ? downloadSourceJarFile(entry) : null;\n    }\n\n    private static boolean isActivated(API api) {\n        return !\"false\".equals(api.getPreferences().get(MavenOrgSourceLoaderPreferencesProvider.ACTIVATED));\n    }\n\n    protected String searchSource(Container.Entry entry, File sourceJarFile) {\n        if (sourceJarFile != null) {\n            byte[] buffer = new byte[1024 * 2];\n\n            try (ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(sourceJarFile)))) {\n                ZipEntry ze = zis.getNextEntry();\n                String name = entry.getPath();\n\n                name = name.substring(0, name.length()-6) + \".java\"; // 6 = \".class\".length()\n\n                while (ze != null) {\n                    if (ze.getName().equals(name)) {\n                        ByteArrayOutputStream out = new ByteArrayOutputStream();\n                        int read = zis.read(buffer);\n\n                        while (read > 0) {\n                            out.write(buffer, 0, read);\n                            read = zis.read(buffer);\n                        }\n\n                        return new String(out.toByteArray(), \"UTF-8\");\n                    }\n\n                    ze = zis.getNextEntry();\n                }\n\n                zis.closeEntry();\n            } catch (IOException e) {\n                assert ExceptionUtil.printStackTrace(e);\n            }\n        }\n\n        return null;\n    }\n\n    protected File downloadSourceJarFile(Container.Entry entry) {\n        if (cache.containsKey(entry)) {\n            return cache.get(entry);\n        }\n\n        if (!entry.isDirectory() && !failed.contains(entry)) {\n            try {\n                // SHA-1\n                MessageDigest messageDigest = MessageDigest.getInstance(\"SHA-1\");\n                byte[] buffer = new byte[1024 * 2];\n\n                try (DigestInputStream is = new DigestInputStream(entry.getInputStream(), messageDigest)) {\n                    while (is.read(buffer) > -1);\n                }\n\n                byte[] array = messageDigest.digest();\n                StringBuilder sb = new StringBuilder();\n\n                for (byte b : array) {\n                    sb.append(hexa((b & 255) >> 4));\n                    sb.append(hexa(b & 15));\n                }\n\n                String sha1 = sb.toString();\n\n                // Search artifact on maven.org\n                URL searchUrl = new URL(MAVENORG_SEARCH_URL_PREFIX + sha1 + MAVENORG_SEARCH_URL_SUFFIX);\n                boolean sourceAvailable = false;\n                String id = null;\n                String numFound = null;\n\n                try (InputStream is = searchUrl.openStream()) {\n                    XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(is);\n                    String name = \"\";\n\n                    while (reader.hasNext()) {\n                        switch (reader.next()) {\n                            case XMLStreamConstants.START_ELEMENT:\n                                if (\"str\".equals(reader.getLocalName())) {\n                                    if (\"id\".equals(reader.getAttributeValue(null, \"name\"))) {\n                                        name = \"id\";\n                                    } else {\n                                        name = \"str\";\n                                    }\n                                } else if (\"result\".equals(reader.getLocalName())) {\n                                    numFound = reader.getAttributeValue(null, \"numFound\");\n                                } else {\n                                    name = \"\";\n                                }\n                                break;\n                            case XMLStreamConstants.CHARACTERS:\n                                switch (name) {\n                                    case \"id\":\n                                        id = reader.getText().trim();\n                                        break;\n                                    case \"str\":\n                                        sourceAvailable |= \"-sources.jar\".equals(reader.getText().trim());\n                                        break;\n                                }\n                                break;\n                        }\n                    }\n\n                    reader.close();\n                }\n\n                String groupId=null, artifactId=null, version=null;\n\n                if (\"0\".equals(numFound)) {\n                    // File not indexed by Apache Solr of maven.org -> Try to found groupId, artifactId, version in 'pom.properties'\n                    Properties pomProperties = getPomProperties(entry);\n\n                    if (pomProperties != null) {\n                        groupId = pomProperties.getProperty(\"groupId\");\n                        artifactId = pomProperties.getProperty(\"artifactId\");\n                        version = pomProperties.getProperty(\"version\");\n                    }\n                } else if (\"1\".equals(numFound) && sourceAvailable) {\n                    int index1 = id.indexOf(':');\n                    int index2 = id.lastIndexOf(':');\n\n                    groupId = id.substring(0, index1);\n                    artifactId = id.substring(index1+1, index2);\n                    version = id.substring(index2+1);\n                }\n\n                if (artifactId != null) {\n                    // Load source\n                    String filePath = groupId.replace('.', '/') + '/' + artifactId + '/' + version + '/' + artifactId + '-' + version;\n                    URL loadUrl = new URL(MAVENORG_LOAD_URL_PREFIX + filePath + MAVENORG_LOAD_URL_SUFFIX);\n                    File tmpFile = File.createTempFile(\"jd-gui.tmp.\", '.' + groupId + '_' + artifactId + '_' + version + \"-sources.jar\");\n\n                    tmpFile.delete();\n                    tmpFile.deleteOnExit();\n\n                    try (InputStream is = new BufferedInputStream(loadUrl.openStream()); OutputStream os = new BufferedOutputStream(new FileOutputStream(tmpFile))) {\n                        int read = is.read(buffer);\n                        while (read > 0) {\n                            os.write(buffer, 0, read);\n                            read = is.read(buffer);\n                        }\n                    }\n\n                    cache.put(entry, tmpFile);\n                    return tmpFile;\n                }\n            } catch (Exception e) {\n                assert ExceptionUtil.printStackTrace(e);\n            }\n        }\n\n        failed.add(entry);\n        return null;\n    }\n\n    private static Properties getPomProperties(Container.Entry parent) {\n        // Search 'META-INF/maven/*/*/pom.properties'\n        for (Container.Entry child1 : parent.getChildren()) {\n            if (child1.isDirectory() && child1.getPath().equals(\"META-INF\")) {\n                for (Container.Entry child2 : child1.getChildren()) {\n                    if (child2.isDirectory() && child2.getPath().equals(\"META-INF/maven\")) {\n                        if (child2.isDirectory()) {\n                            Collection<Container.Entry> children = child2.getChildren();\n                            if (children.size() == 1) {\n                                Container.Entry entry = children.iterator().next();\n                                if (entry.isDirectory()) {\n                                    children = entry.getChildren();\n                                    if (children.size() == 1) {\n                                        entry = children.iterator().next();\n                                        for (Container.Entry child3 : entry.getChildren()) {\n                                            if (!child3.isDirectory() && child3.getPath().endsWith(\"/pom.properties\")) {\n                                                // Load properties\n                                                try (InputStream is = child3.getInputStream()) {\n                                                    Properties properties = new Properties();\n                                                    properties.load(is);\n                                                    return properties;\n                                                } catch (Exception e) {\n                                                    assert ExceptionUtil.printStackTrace(e);\n                                                }\n                                            }\n                                        }\n                                    }\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n        }\n\n        return null;\n    }\n\n    private static char hexa(int i) { return (char)( (i <= 9) ? ('0' + i) : (('a' - 10) + i) ); }\n\n    protected boolean accepted(String filters, String path) {\n        // 'filters' example : '+org +com.google +com.ibm +com.jcraft +com.springsource +com.sun -com +java +javax +sun +sunw'\n        StringTokenizer tokenizer = new StringTokenizer(filters);\n\n        while (tokenizer.hasMoreTokens()) {\n            String filter = tokenizer.nextToken();\n\n            if (filter.length() > 1) {\n                String prefix = filter.substring(1).replace('.', '/');\n\n                if (prefix.charAt(prefix.length() - 1) != '/') {\n                    prefix += '/';\n                }\n\n                if (path.startsWith(prefix)) {\n                    return (filter.charAt(0) == '+');\n                }\n            }\n        }\n\n        return false;\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/sourcesaver/AbstractSourceSaverProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.sourcesaver;\n\nimport org.jd.gui.spi.SourceSaver;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Properties;\nimport java.util.regex.Pattern;\n\npublic abstract class AbstractSourceSaverProvider implements SourceSaver {\n    protected List<String> externalSelectors;\n    protected Pattern externalPathPattern;\n\n    /**\n     * Initialize \"selectors\" and \"pathPattern\" with optional external properties file\n     */\n    public AbstractSourceSaverProvider() {\n        Properties properties = new Properties();\n        Class clazz = this.getClass();\n\n        try (InputStream is = clazz.getClassLoader().getResourceAsStream(clazz.getName().replace('.', '/') + \".properties\")) {\n            if (is != null) {\n                properties.load(is);\n            }\n        } catch (IOException e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n\n        init(properties);\n    }\n\n    protected void init(Properties properties) {\n        String selectors = properties.getProperty(\"selectors\");\n\n        if (selectors != null) {\n            externalSelectors = Arrays.asList(selectors.split(\",\"));\n        }\n\n        String pathRegExp = properties.getProperty(\"pathRegExp\");\n\n        if (pathRegExp != null) {\n            externalPathPattern = Pattern.compile(pathRegExp);\n        }\n    }\n\n    protected String[] appendSelectors(String selector) {\n        if (externalSelectors == null) {\n            return new String[] { selector };\n        } else {\n            int size = externalSelectors.size();\n            String[] array = new String[size+1];\n            externalSelectors.toArray(array);\n            array[size] = selector;\n            return array;\n        }\n    }\n\n    protected String[] appendSelectors(String... selectors) {\n        if (externalSelectors == null) {\n            return selectors;\n        } else {\n            int size = externalSelectors.size();\n            String[] array = new String[size+selectors.length];\n            externalSelectors.toArray(array);\n            System.arraycopy(selectors, 0, array, size, selectors.length);\n            return array;\n        }\n    }\n\n    public Pattern getPathPattern() { return externalPathPattern; }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/sourcesaver/ClassFileSourceSaverProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.sourcesaver;\n\nimport org.jd.core.v1.ClassFileToJavaSourceDecompiler;\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.util.decompiler.ContainerLoader;\nimport org.jd.gui.util.decompiler.LineNumberStringBuilderPrinter;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.util.io.NewlineOutputStream;\n\nimport java.io.*;\nimport java.nio.charset.Charset;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class ClassFileSourceSaverProvider extends AbstractSourceSaverProvider {\n    protected static final String ESCAPE_UNICODE_CHARACTERS = \"ClassFileDecompilerPreferences.escapeUnicodeCharacters\";\n    protected static final String REALIGN_LINE_NUMBERS      = \"ClassFileDecompilerPreferences.realignLineNumbers\";\n    protected static final String WRITE_LINE_NUMBERS        = \"ClassFileSaverPreferences.writeLineNumbers\";\n    protected static final String WRITE_METADATA            = \"ClassFileSaverPreferences.writeMetadata\";\n    protected static final String JD_CORE_VERSION           = \"JdGuiPreferences.jdCoreVersion\";\n\n    protected static final ClassFileToJavaSourceDecompiler DECOMPILER = new ClassFileToJavaSourceDecompiler();\n\n    protected ContainerLoader loader = new ContainerLoader();\n    protected LineNumberStringBuilderPrinter printer = new LineNumberStringBuilderPrinter();\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.class\"); }\n\n    @Override\n    public String getSourcePath(Container.Entry entry) {\n        String path = entry.getPath();\n        int index = path.lastIndexOf('.');\n        String prefix = (index == -1) ? path : path.substring(0, index);\n        return prefix + \".java\";\n    }\n\n    @Override\n    public int getFileCount(API api, Container.Entry entry) {\n        if (entry.getPath().indexOf('$') == -1) {\n            return 1;\n        } else {\n            return 0;\n        }\n    }\n\n    @Override\n    public void save(API api, Controller controller, Listener listener, Path rootPath, Container.Entry entry) {\n        String sourcePath = getSourcePath(entry);\n        Path path = rootPath.resolve(sourcePath);\n\n        saveContent(api, controller, listener, rootPath, path, entry);\n    }\n\n    @Override\n    public void saveContent(API api, Controller controller, Listener listener, Path rootPath, Path path, Container.Entry entry) {\n        try {\n            // Call listener\n            if (path.toString().indexOf('$') == -1) {\n                listener.pathSaved(path);\n            }\n            // Init preferences\n            Map<String, String> preferences = api.getPreferences();\n            boolean realignmentLineNumbers = getPreferenceValue(preferences, REALIGN_LINE_NUMBERS, true);\n            boolean unicodeEscape = getPreferenceValue(preferences, ESCAPE_UNICODE_CHARACTERS, false);\n            boolean showLineNumbers = getPreferenceValue(preferences, WRITE_LINE_NUMBERS, true);\n\n            Map<String, Object> configuration = new HashMap<>();\n            configuration.put(\"realignLineNumbers\", realignmentLineNumbers);\n\n            // Init loader\n            loader.setEntry(entry);\n\n            // Init printer\n            printer.setRealignmentLineNumber(realignmentLineNumbers);\n            printer.setUnicodeEscape(unicodeEscape);\n            printer.setShowLineNumbers(showLineNumbers);\n\n            // Format internal name\n            String entryPath = entry.getPath();\n            assert entryPath.endsWith(\".class\");\n            String entryInternalName = entryPath.substring(0, entryPath.length() - 6); // 6 = \".class\".length()\n\n            // Decompile class file\n            DECOMPILER.decompile(loader, printer, entryInternalName, configuration);\n\n            StringBuilder stringBuffer = printer.getStringBuffer();\n\n            // Metadata\n            if (getPreferenceValue(preferences, WRITE_METADATA, true)) {\n                // Add location\n                String location =\n                    new File(entry.getUri()).getPath()\n                    // Escape \"\\ u\" sequence to prevent \"Invalid unicode\" errors\n                    .replaceAll(\"(^|[^\\\\\\\\])\\\\\\\\u\", \"\\\\\\\\\\\\\\\\u\");\n                stringBuffer.append(\"\\n\\n/* Location:              \");\n                stringBuffer.append(location);\n                // Add Java compiler version\n                int majorVersion = printer.getMajorVersion();\n\n                if (majorVersion >= 45) {\n                    stringBuffer.append(\"\\n * Java compiler version: \");\n\n                    if (majorVersion >= 49) {\n                        stringBuffer.append(majorVersion - (49 - 5));\n                    } else {\n                        stringBuffer.append(majorVersion - (45 - 1));\n                    }\n\n                    stringBuffer.append(\" (\");\n                    stringBuffer.append(majorVersion);\n                    stringBuffer.append('.');\n                    stringBuffer.append(printer.getMinorVersion());\n                    stringBuffer.append(')');\n                }\n                // Add JD-Core version\n                stringBuffer.append(\"\\n * JD-Core Version:       \");\n                stringBuffer.append(preferences.get(JD_CORE_VERSION));\n                stringBuffer.append(\"\\n */\");\n            }\n\n            try (PrintStream ps = new PrintStream(new NewlineOutputStream(Files.newOutputStream(path)), true, \"UTF-8\")) {\n                ps.print(stringBuffer.toString());\n            } catch (IOException e) {\n                assert ExceptionUtil.printStackTrace(e);\n            }\n        } catch (Throwable t) {\n            assert ExceptionUtil.printStackTrace(t);\n\n            try (BufferedWriter writer = Files.newBufferedWriter(path, Charset.defaultCharset())) {\n                writer.write(\"// INTERNAL ERROR //\");\n            } catch (IOException ee) {\n                assert ExceptionUtil.printStackTrace(ee);\n            }\n        }\n    }\n\n    protected static boolean getPreferenceValue(Map<String, String> preferences, String key, boolean defaultValue) {\n        String v = preferences.get(key);\n        return (v == null) ? defaultValue : Boolean.valueOf(v);\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/sourcesaver/DirectorySourceSaverProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.sourcesaver;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.spi.SourceSaver;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Collection;\n\npublic class DirectorySourceSaverProvider extends AbstractSourceSaverProvider {\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:dir:*\"); }\n\n    @Override public String getSourcePath(Container.Entry entry) { return entry.getPath() + \".src.zip\"; }\n\n    @Override public int getFileCount(API api, Container.Entry entry) { return getFileCount(api, entry.getChildren()); }\n\n    protected int getFileCount(API api, Collection<Container.Entry> entries) {\n        int count = 0;\n\n        for (Container.Entry e : entries) {\n            SourceSaver sourceSaver = api.getSourceSaver(e);\n\n            if (sourceSaver != null) {\n                count += sourceSaver.getFileCount(api, e);\n            }\n        }\n\n        return count;\n    }\n\n    @Override\n    public void save(API api, SourceSaver.Controller controller, SourceSaver.Listener listener, Path rootPath, Container.Entry entry) {\n        Path path = rootPath.resolve(entry.getPath());\n\n        try {\n            Files.createDirectories(path);\n            saveContent(api, controller, listener, rootPath, path, entry);\n        } catch (IOException e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n    }\n\n    @Override\n    public void saveContent(API api, SourceSaver.Controller controller, SourceSaver.Listener listener, Path rootPath, Path path, Container.Entry entry) {\n        for (Container.Entry e : getChildren(entry)) {\n            if (controller.isCancelled()) {\n                break;\n            }\n\n            SourceSaver sourceSaver = api.getSourceSaver(e);\n\n            if (sourceSaver != null) {\n                sourceSaver.save(api, controller, listener, rootPath, e);\n            }\n        }\n    }\n\n    protected Collection<Container.Entry> getChildren(Container.Entry entry) { return entry.getChildren(); }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/sourcesaver/FileSourceSaverProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.sourcesaver;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.spi.SourceSaver;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport java.io.BufferedWriter;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.charset.Charset;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.nio.file.StandardCopyOption;\n\npublic class FileSourceSaverProvider extends AbstractSourceSaverProvider {\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*\"); }\n\n    @Override public String getSourcePath(Container.Entry entry) { return entry.getPath(); }\n\n    @Override public int getFileCount(API api, Container.Entry entry) { return 1; }\n\n    @Override\n    public void save(API api, SourceSaver.Controller controller, SourceSaver.Listener listener, Path rootPath, Container.Entry entry) {\n        saveContent(api, controller, listener, rootPath, rootPath.resolve(entry.getPath()), entry);\n    }\n\n    @Override\n    public void saveContent(API api, SourceSaver.Controller controller, SourceSaver.Listener listener, Path rootPath, Path path, Container.Entry entry) {\n        listener.pathSaved(path);\n\n        try (InputStream is = entry.getInputStream()) {\n            Files.copy(is, path, StandardCopyOption.REPLACE_EXISTING);\n        } catch (IOException e) {\n            assert ExceptionUtil.printStackTrace(e);\n\n            try (BufferedWriter writer = Files.newBufferedWriter(path, Charset.defaultCharset())) {\n                writer.write(\"// INTERNAL ERROR //\");\n            } catch (IOException ee) {\n                assert ExceptionUtil.printStackTrace(ee);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/sourcesaver/PackageSourceSaverProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.sourcesaver;\n\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.util.container.JarContainerEntryUtil;\n\nimport java.util.Collection;\n\npublic class PackageSourceSaverProvider extends DirectorySourceSaverProvider {\n\n    @Override public String[] getSelectors() { return appendSelectors(\"jar:dir:*\", \"war:dir:*\", \"ear:dir:*\"); }\n\n    @Override\n    protected Collection<Container.Entry> getChildren(Container.Entry entry) {\n        return JarContainerEntryUtil.removeInnerTypeEntries(entry.getChildren());\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/sourcesaver/ZipFileSourceSaverProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.sourcesaver;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.spi.SourceSaver;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport java.io.File;\nimport java.net.URI;\nimport java.nio.file.FileSystem;\nimport java.nio.file.FileSystems;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.HashMap;\n\npublic class ZipFileSourceSaverProvider extends DirectorySourceSaverProvider {\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.zip\", \"*:file:*.jar\", \"*:file:*.war\", \"*:file:*.ear\", \"*:file:*.aar\", \"*:file:*.jmod\", \"*:file:*.kar\"); }\n\n    @Override\n    public void save(API api, SourceSaver.Controller controller, SourceSaver.Listener listener, Path rootPath, Container.Entry entry) {\n        try {\n            String sourcePath = getSourcePath(entry);\n            Path path = rootPath.resolve(sourcePath);\n            Path parentPath = path.getParent();\n\n            if ((parentPath != null) && !Files.exists(parentPath)) {\n                Files.createDirectories(parentPath);\n            }\n\n            File tmpSourceFile = api.loadSourceFile(entry);\n\n            if (tmpSourceFile != null) {\n                Files.copy(tmpSourceFile.toPath(), path);\n            } else {\n                File tmpFile = File.createTempFile(\"jd-gui.\", \".tmp.zip\");\n\n                tmpFile.delete();\n                tmpFile.deleteOnExit();\n\n                URI tmpFileUri = tmpFile.toURI();\n                URI tmpArchiveUri = new URI(\"jar:\" + tmpFileUri.getScheme(), tmpFileUri.getHost(), tmpFileUri.getPath() + \"!/\", null);\n\n                HashMap<String, String> env = new HashMap<>();\n                env.put(\"create\", \"true\");\n\n                FileSystem tmpArchiveFs = FileSystems.newFileSystem(tmpArchiveUri, env);\n                Path tmpArchiveRootPath = tmpArchiveFs.getPath(\"/\");\n\n                saveContent(api, controller, listener, tmpArchiveRootPath, tmpArchiveRootPath, entry);\n\n                tmpArchiveFs.close();\n\n                Files.move(tmpFile.toPath(), path);\n            }\n        } catch (Exception e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/AbstractTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.jd.gui.spi.TreeNodeFactory;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Properties;\nimport java.util.regex.Pattern;\n\npublic abstract class AbstractTreeNodeFactoryProvider implements TreeNodeFactory {\n    protected List<String> externalSelectors;\n    protected Pattern externalPathPattern;\n\n    /**\n     * Initialize \"selectors\" and \"pathPattern\" with optional external properties file\n     */\n    public AbstractTreeNodeFactoryProvider() {\n        Properties properties = new Properties();\n        Class clazz = this.getClass();\n\n        try (InputStream is = clazz.getClassLoader().getResourceAsStream(clazz.getName().replace('.', '/') + \".properties\")) {\n            if (is != null) {\n                properties.load(is);\n            }\n        } catch (IOException e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n\n        init(properties);\n    }\n\n    protected void init(Properties properties) {\n        String selectors = properties.getProperty(\"selectors\");\n\n        if (selectors != null) {\n            externalSelectors = Arrays.asList(selectors.split(\",\"));\n        }\n\n        String pathRegExp = properties.getProperty(\"pathRegExp\");\n\n        if (pathRegExp != null) {\n            externalPathPattern = Pattern.compile(pathRegExp);\n        }\n    }\n\n    protected String[] appendSelectors(String selector) {\n        if (externalSelectors == null) {\n            return new String[] { selector };\n        } else {\n            int size = externalSelectors.size();\n            String[] array = new String[size+1];\n            externalSelectors.toArray(array);\n            array[size] = selector;\n            return array;\n        }\n    }\n\n    protected String[] appendSelectors(String... selectors) {\n        if (externalSelectors == null) {\n            return selectors;\n        } else {\n            int size = externalSelectors.size();\n            String[] array = new String[size+selectors.length];\n            externalSelectors.toArray(array);\n            System.arraycopy(selectors, 0, array, size, selectors.length);\n            return array;\n        }\n    }\n\n    @Override public Pattern getPathPattern() { return externalPathPattern; }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/AbstractTypeFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.PageCreator;\nimport org.jd.gui.api.feature.TreeNodeExpandable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Type;\nimport org.jd.gui.spi.TypeFactory;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Comparator;\n\npublic abstract class AbstractTypeFileTreeNodeFactoryProvider extends AbstractTreeNodeFactoryProvider {\n    protected static final TypeComparator TYPE_COMPARATOR = new TypeComparator();\n    protected static final FieldOrMethodBeanComparator FIELD_OR_METHOD_BEAN_COMPARATOR = new FieldOrMethodBeanComparator();\n\n    public static class BaseTreeNode extends DefaultMutableTreeNode implements ContainerEntryGettable, UriGettable, PageCreator {\n        protected Container.Entry entry;\n        protected PageAndTipFactory factory;\n        protected URI uri;\n\n        public BaseTreeNode(Container.Entry entry, String fragment, Object userObject, PageAndTipFactory factory) {\n            super(userObject);\n            this.entry = entry;\n            this.factory = factory;\n\n            if (fragment != null) {\n                try {\n                    URI uri = entry.getUri();\n                    this.uri = new URI(uri.getScheme(), uri.getHost(), uri.getPath(), fragment);\n                } catch (URISyntaxException e) {\n                    assert ExceptionUtil.printStackTrace(e);\n                }\n            } else {\n                this.uri = entry.getUri();\n            }\n        }\n\n        // --- ContainerEntryGettable --- //\n        @Override public Container.Entry getEntry() { return entry; }\n\n        // --- UriGettable --- //\n        @Override public URI getUri() { return uri; }\n\n        // --- PageCreator --- //\n        @Override\n        public <T extends JComponent & UriGettable> T createPage(API api) {\n            // Lazy 'tip' initialization\n            ((TreeNodeBean)userObject).setTip(factory.makeTip(api, entry));\n            return factory.makePage(api, entry);\n        }\n    }\n\n    protected static class FileTreeNode extends BaseTreeNode implements TreeNodeExpandable {\n        protected boolean initialized;\n\n        public FileTreeNode(Container.Entry entry, Object userObject, PageAndTipFactory pageAndTipFactory) {\n            this(entry, null, userObject, pageAndTipFactory);\n        }\n\n        public FileTreeNode(Container.Entry entry, String fragment, Object userObject, PageAndTipFactory factory) {\n            super(entry, fragment, userObject, factory);\n            initialized = false;\n            // Add dummy node\n            add(new DefaultMutableTreeNode());\n        }\n\n        // --- TreeNodeExpandable --- //\n        @Override\n        public void populateTreeNode(API api) {\n            if (!initialized) {\n                removeAllChildren();\n                // Create type node\n                TypeFactory typeFactory = api.getTypeFactory(entry);\n\n                if (typeFactory != null) {\n                    Collection<Type> types = typeFactory.make(api, entry);\n\n                    for (Type type : types) {\n                        add(new TypeTreeNode(entry, type, new TreeNodeBean(type.getDisplayTypeName(), type.getIcon()), factory));\n                    }\n                }\n                \n                initialized = true;\n            }\n        }\n    }\n\n    protected static class TypeTreeNode extends BaseTreeNode implements TreeNodeExpandable {\n        protected boolean initialized;\n        protected Type type;\n\n        public TypeTreeNode(Container.Entry entry, Type type, Object userObject, PageAndTipFactory factory) {\n            super(entry, type.getName(), userObject, factory);\n            this.initialized = false;\n            this.type = type;\n            // Add dummy node\n            add(new DefaultMutableTreeNode());\n        }\n\n        // --- TreeNodeExpandable --- //\n        @Override\n        public void populateTreeNode(API api) {\n            if (!initialized) {\n                removeAllChildren();\n\n                String typeName = type.getName();\n\n                // Create inner types\n                Collection<Type> innerTypes = type.getInnerTypes();\n\n                if (innerTypes != null) {\n                    ArrayList<Type> innerTypeList = new ArrayList<>(innerTypes);\n                    innerTypeList.sort(TYPE_COMPARATOR);\n\n                    for (Type innerType : innerTypeList) {\n                        add(new TypeTreeNode(entry, innerType, new TreeNodeBean(innerType.getDisplayInnerTypeName(), innerType.getIcon()), factory));\n                    }\n                }\n\n                // Create fields\n                Collection<Type.Field> fields = type.getFields();\n\n                if (fields != null) {\n                    ArrayList<FieldOrMethodBean> beans = new ArrayList<>(fields.size());\n\n                    for (Type.Field field : fields) {\n                        String fragment = typeName + '-' + field.getName() + '-' + field.getDescriptor();\n                        beans.add(new FieldOrMethodBean(fragment, field.getDisplayName(), field.getIcon()));\n                    }\n\n                    beans.sort(FIELD_OR_METHOD_BEAN_COMPARATOR);\n\n                    for (FieldOrMethodBean bean : beans) {\n                        add(new FieldOrMethodTreeNode(entry, bean.fragment, new TreeNodeBean(bean.label, bean.icon), factory));\n                    }\n                }\n\n                // Create methods\n                Collection<Type.Method> methods = type.getMethods();\n\n                if (methods != null) {\n                    ArrayList<FieldOrMethodBean> beans = new ArrayList<>();\n\n                    for (Type.Method method : methods) {\n                        if (!method.getName().equals(\"<clinit>\")) {\n                            String fragment = typeName + '-' + method.getName() + '-' + method.getDescriptor();\n                            beans.add(new FieldOrMethodBean(fragment, method.getDisplayName(), method.getIcon()));\n                        }\n                    }\n\n                    beans.sort(FIELD_OR_METHOD_BEAN_COMPARATOR);\n\n                    for (FieldOrMethodBean bean : beans) {\n                        add(new FieldOrMethodTreeNode(entry, bean.fragment, new TreeNodeBean(bean.label, bean.icon), factory));\n                    }\n                }\n\n                initialized = true;\n            }\n        }\n    }\n\n    protected static class FieldOrMethodTreeNode extends BaseTreeNode {\n        public FieldOrMethodTreeNode(Container.Entry entry, String fragment, Object userObject, PageAndTipFactory factory) {\n            super(entry, fragment, userObject, factory);\n        }\n    }\n\n    protected static class FieldOrMethodBean {\n        public String fragment, label;\n        public Icon icon;\n\n        public FieldOrMethodBean(String fragment, String label, Icon icon) {\n            this.fragment = fragment;\n            this.label = label;\n            this.icon = icon;\n        }\n    }\n\n    protected interface PageAndTipFactory {\n        <T extends JComponent & UriGettable> T makePage(API api, Container.Entry entry);\n        String makeTip(API api, Container.Entry entry);\n    }\n\n    protected static class TypeComparator implements Comparator<Type> {\n        @Override\n        public int compare(Type type1, Type type2) {\n            return type1.getName().compareTo(type2.getName());\n        }\n    }\n\n    protected static class FieldOrMethodBeanComparator implements Comparator<FieldOrMethodBean> {\n        @Override\n        public int compare(FieldOrMethodBean bean1, FieldOrMethodBean bean2) {\n            return bean1.label.compareTo(bean2.label);\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/ClassFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.view.component.DynamicPage;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.EOFException;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.regex.Pattern;\n\npublic class ClassFileTreeNodeFactoryProvider extends AbstractTypeFileTreeNodeFactoryProvider {\n    protected static final ImageIcon CLASS_FILE_ICON = new ImageIcon(ClassFileTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/classf_obj.png\"));\n    protected static final Factory FACTORY = new Factory();\n\n    static {\n        // Early class loading\n        try {\n            Class.forName(DynamicPage.class.getName());\n        } catch (Exception e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n    }\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.class\"); }\n\n    @Override\n    public Pattern getPathPattern() {\n        if (externalPathPattern == null) {\n            return Pattern.compile(\"^((?!module-info\\\\.class).)*$\");\n        } else {\n            return externalPathPattern;\n        }\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        int lastSlashIndex = entry.getPath().lastIndexOf('/');\n        String label = entry.getPath().substring(lastSlashIndex+1);\n        return (T)new FileTreeNode(entry, new TreeNodeBean(label, CLASS_FILE_ICON), FACTORY);\n    }\n\n    protected static class Factory implements AbstractTypeFileTreeNodeFactoryProvider.PageAndTipFactory {\n        @Override\n        @SuppressWarnings(\"unchecked\")\n        public <T extends JComponent & UriGettable> T makePage(API a, Container.Entry e) {\n            return (T)new DynamicPage(a, e);\n        }\n\n        @Override\n        public String makeTip(API api, Container.Entry entry) {\n            String location = new File(entry.getUri()).getPath();\n            StringBuilder tip = new StringBuilder(\"<html>Location: \");\n\n            tip.append(location);\n            tip.append(\"<br>Java compiler version: \");\n\n            try (InputStream is = entry.getInputStream()) {\n                is.skip(4); // Skip magic number\n                int minorVersion = readUnsignedShort(is);\n                int majorVersion = readUnsignedShort(is);\n\n                if (majorVersion >= 49) {\n                    tip.append(majorVersion - (49-5));\n                } else if (majorVersion >= 45) {\n                    tip.append(\"1.\");\n                    tip.append(majorVersion - (45-1));\n                }\n                tip.append(\" (\");\n                tip.append(majorVersion);\n                tip.append('.');\n                tip.append(minorVersion);\n                tip.append(')');\n            } catch (IOException e) {\n                assert ExceptionUtil.printStackTrace(e);\n            }\n\n            tip.append(\"</html>\");\n\n            return tip.toString();\n        }\n\n        /**\n         * @see java.io.DataInputStream#readUnsignedShort()\n         */\n        protected int readUnsignedShort(InputStream is) throws IOException {\n            int ch1 = is.read();\n            int ch2 = is.read();\n            if ((ch1 | ch2) < 0)\n                throw new EOFException();\n            return (ch1 << 8) + (ch2 << 0);\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/ClassesDirectoryTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use, \n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport javax.swing.*;\n\npublic class ClassesDirectoryTreeNodeFactoryProvider extends DirectoryTreeNodeFactoryProvider {\n    protected static final ImageIcon ICON = new ImageIcon(ClassesDirectoryTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/packagefolder_obj.png\"));\n\n    @Override public String[] getSelectors() {\n        return appendSelectors(\n                \"jar:dir:META-INF/versions\",\n                \"jar:dir:META-INF/versions/5\",\n                \"jar:dir:META-INF/versions/6\",\n                \"jar:dir:META-INF/versions/7\",\n                \"jar:dir:META-INF/versions/8\",\n                \"jar:dir:META-INF/versions/9\",\n                \"jar:dir:META-INF/versions/10\",\n                \"jar:dir:META-INF/versions/11\",\n                \"jar:dir:META-INF/versions/12\",\n                \"jar:dir:META-INF/versions/13\",\n                \"jar:dir:META-INF/versions/14\",\n                \"war:dir:WEB-INF/classes\",\n                \"jmod:dir:classes\");\n    }\n\n    @Override public ImageIcon getIcon() { return ICON; }\n    @Override public ImageIcon getOpenIcon() { return null; }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/CssFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.fife.ui.rsyntaxtextarea.SyntaxConstants;\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.File;\n\npublic class CssFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider {\n    protected static final ImageIcon ICON = new ImageIcon(HtmlFileTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/css_obj.png\"));\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.css\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        int lastSlashIndex = entry.getPath().lastIndexOf(\"/\");\n        String label = entry.getPath().substring(lastSlashIndex+1);\n        String location = new File(entry.getUri()).getPath();\n        return (T)new TreeNode(entry, new TreeNodeBean(label, \"Location: \" + location, ICON));\n    }\n\n    protected static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode {\n        public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); }\n\n        // --- PageCreator --- //\n        @Override\n        @SuppressWarnings(\"unchecked\")\n        public <T extends JComponent & UriGettable> T createPage(API api) {\n            return (T)new TextFileTreeNodeFactoryProvider.Page(entry) {\n                @Override public String getSyntaxStyle() {\n                    return SyntaxConstants.SYNTAX_STYLE_CSS;\n                }\n            };\n        }\n    }\n}"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/DirectoryTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.TreeNodeExpandable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Container.Entry;\nimport org.jd.gui.spi.TreeNodeFactory;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.File;\nimport java.net.URI;\nimport java.util.Collection;\n\npublic class DirectoryTreeNodeFactoryProvider extends AbstractTreeNodeFactoryProvider {\n    protected static final ImageIcon ICON = new ImageIcon(DirectoryTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/folder.gif\"));\n    protected static final ImageIcon OPEN_ICON = new ImageIcon(DirectoryTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/folder_open.png\"));\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:dir:*\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        int lastSlashIndex = entry.getPath().lastIndexOf('/');\n        Collection<Entry> entries = entry.getChildren();\n\n        // Aggregate directory names\n        while (entries.size() == 1) {\n            Entry child = entries.iterator().next();\n            if (!child.isDirectory() || api.getTreeNodeFactory(child) != this || entry.getContainer() != child.getContainer()) break;\n            entry = child;\n            entries = entry.getChildren();\n        }\n\n        String label = entry.getPath().substring(lastSlashIndex+1);\n        String location = new File(entry.getUri()).getPath();\n        TreeNode node = new TreeNode(entry, new TreeNodeBean(label, \"Location: \" + location, getIcon(), getOpenIcon()));\n\n        if (entries.size() > 0) {\n            // Add dummy node\n            node.add(new DefaultMutableTreeNode());\n        }\n\n        return (T)node;\n    }\n\n    public ImageIcon getIcon() { return ICON; }\n    public ImageIcon getOpenIcon() { return OPEN_ICON; }\n\n    protected static class TreeNode extends DefaultMutableTreeNode implements ContainerEntryGettable, UriGettable, TreeNodeExpandable {\n        Container.Entry entry;\n        boolean initialized;\n\n        public TreeNode(Container.Entry entry, Object userObject) {\n            super(userObject);\n            this.entry = entry;\n            this.initialized = false;\n        }\n\n        // --- ContainerEntryGettable --- //\n        @Override public Container.Entry getEntry() { return entry; }\n\n        // --- UriGettable --- //\n        @Override public URI getUri() { return entry.getUri(); }\n\n        // --- TreeNodeExpandable --- //\n        @Override\n        public void populateTreeNode(API api) {\n            if (!initialized) {\n                removeAllChildren();\n\n                Collection<Container.Entry> entries = getChildren();\n\n                while (entries.size() == 1) {\n                    Entry child = entries.iterator().next();\n                    if (!child.isDirectory() || api.getTreeNodeFactory(child) != this) {\n                        break;\n                    }\n                    entries = child.getChildren();\n                }\n\n                for (Entry entry : entries) {\n                    TreeNodeFactory factory = api.getTreeNodeFactory(entry);\n                    if (factory != null) {\n                        add(factory.make(api, entry));\n                    }\n                }\n\n                initialized = true;\n            }\n        }\n\n        public Collection<Container.Entry> getChildren() { return entry.getChildren(); }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/DtdFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.fife.ui.rsyntaxtextarea.SyntaxConstants;\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.File;\n\npublic class DtdFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider {\n    protected static final ImageIcon ICON = new ImageIcon(DtdFileTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/dtd_obj.gif\"));\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.dtd\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        int lastSlashIndex = entry.getPath().lastIndexOf(\"/\");\n        String label = entry.getPath().substring(lastSlashIndex+1);\n        String location = new File(entry.getUri()).getPath();\n        return (T)new TreeNode(entry, new TreeNodeBean(label, \"Location: \" + location, ICON));\n    }\n\n    protected static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode {\n        public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); }\n\n        // --- PageCreator --- //\n        @Override\n        @SuppressWarnings(\"unchecked\")\n        public <T extends JComponent & UriGettable> T createPage(API api) {\n            return (T)new TextFileTreeNodeFactoryProvider.Page(entry) {\n                @Override public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_DTD; }\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/EarFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.File;\n\npublic class EarFileTreeNodeFactoryProvider extends ZipFileTreeNodeFactoryProvider {\n    protected static final ImageIcon ICON = new ImageIcon(JarFileTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/ear_obj.gif\"));\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.ear\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        int lastSlashIndex = entry.getPath().lastIndexOf(\"/\");\n        String label = entry.getPath().substring(lastSlashIndex+1);\n        String location = new File(entry.getUri()).getPath();\n        T node = (T)new TreeNode(entry, new TreeNodeBean(label, \"Location: \" + location, ICON));\n        // Add dummy node\n        node.add(new DefaultMutableTreeNode());\n        return node;\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/EjbJarXmlFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.PageCreator;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.view.component.EjbJarXmlFilePage;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.File;\n\npublic class EjbJarXmlFileTreeNodeFactoryProvider extends FileTreeNodeFactoryProvider {\n    protected static final ImageIcon ICON = new ImageIcon(ManifestFileTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/xml_obj.gif\"));\n\n    @Override public String[] getSelectors() { return appendSelectors(\"jar:file:META-INF/ejb-jar.xml\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        String location = new File(entry.getUri()).getPath();\n        return (T)new TreeNode(entry, new TreeNodeBean(\"ejb-jar.xml\", \"Location: \" + location, ICON));\n    }\n\n    protected static class TreeNode extends FileTreeNodeFactoryProvider.TreeNode implements PageCreator {\n        public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); }\n\n        // --- PageCreator --- //\n        @Override\n        @SuppressWarnings(\"unchecked\")\n        public <T extends JComponent & UriGettable> T createPage(API api) {\n            return (T)new EjbJarXmlFilePage(api, entry);\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/FileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.File;\nimport java.net.URI;\n\npublic class FileTreeNodeFactoryProvider extends AbstractTreeNodeFactoryProvider {\n    protected static final ImageIcon ICON = new ImageIcon(FileTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/file_plain_obj.png\"));\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        int lastSlashIndex = entry.getPath().lastIndexOf('/');\n        String label = entry.getPath().substring(lastSlashIndex+1);\n        String location = new File(entry.getUri()).getPath();\n        return (T)new TreeNode(entry, new TreeNodeBean(label, \"Location: \" + location, ICON));\n    }\n\n    protected static class TreeNode extends DefaultMutableTreeNode implements ContainerEntryGettable, UriGettable {\n        protected Container.Entry entry;\n\n        public TreeNode(Container.Entry entry, Object userObject) {\n            super(userObject);\n            this.entry = entry;\n        }\n\n        // --- ContainerEntryGettable --- //\n        @Override public Container.Entry getEntry() { return entry; }\n\n        // --- UriGettable --- //\n        @Override public URI getUri() { return entry.getUri(); }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/HtmlFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.fife.ui.rsyntaxtextarea.SyntaxConstants;\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.File;\n\npublic class HtmlFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider {\n    protected static final ImageIcon ICON = new ImageIcon(HtmlFileTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/html_obj.gif\"));\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.html\", \"*:file:*.xhtml\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        int lastSlashIndex = entry.getPath().lastIndexOf(\"/\");\n        String label = entry.getPath().substring(lastSlashIndex+1);\n        String location = new File(entry.getUri()).getPath();\n        return (T)new TreeNode(entry, new TreeNodeBean(label, \"Location: \" + location, ICON));\n    }\n\n    protected static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode {\n        public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); }\n\n        // --- PageCreator --- //\n        @Override\n        @SuppressWarnings(\"unchecked\")\n        public <T extends JComponent & UriGettable> T createPage(API api) {\n            return (T)new TextFileTreeNodeFactoryProvider.Page(entry) {\n                @Override public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_HTML; }\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/ImageFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.*;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.imageio.ImageIO;\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.awt.*;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URI;\n\npublic class ImageFileTreeNodeFactoryProvider extends FileTreeNodeFactoryProvider {\n    protected static final ImageIcon ICON = new ImageIcon(ImageFileTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/file-image.gif\"));\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.gif\", \"*:file:*.jpg\", \"*:file:*.png\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        int lastSlashIndex = entry.getPath().lastIndexOf(\"/\");\n        String label = entry.getPath().substring(lastSlashIndex+1);\n        String location = new File(entry.getUri()).getPath();\n        return (T)new TreeNode(entry, new TreeNodeBean(label, \"Location: \" + location, ICON));\n    }\n\n    protected static class TreeNode extends FileTreeNodeFactoryProvider.TreeNode implements PageCreator {\n        public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); }\n\n        // --- PageCreator --- //\n        @Override\n        @SuppressWarnings(\"unchecked\")\n        public <T extends JComponent & UriGettable> T createPage(API api) {\n            return (T)new ImagePage(entry);\n        }\n    }\n\n    protected static class ImagePage extends JPanel implements UriGettable {\n        protected Container.Entry entry;\n\n        public ImagePage(Container.Entry entry) {\n            super(new BorderLayout());\n\n            this.entry = entry;\n\n            try (InputStream is = entry.getInputStream()) {\n                JScrollPane scrollPane = new JScrollPane(new JLabel(new ImageIcon(ImageIO.read(is))));\n\n                scrollPane.getHorizontalScrollBar().setUnitIncrement(16);\n                scrollPane.getVerticalScrollBar().setUnitIncrement(16);\n\n                add(scrollPane, BorderLayout.CENTER);\n            } catch (IOException e) {\n                assert ExceptionUtil.printStackTrace(e);\n            }\n        }\n\n        // --- UriGettable --- //\n        @Override public URI getUri() { return entry.getUri(); }\n    }\n}"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/JarFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.util.container.JarContainerEntryUtil;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.File;\nimport java.util.Collection;\n\npublic class JarFileTreeNodeFactoryProvider extends ZipFileTreeNodeFactoryProvider {\n    protected static final ImageIcon JAR_FILE_ICON = new ImageIcon(JarFileTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/jar_obj.png\"));\n    protected static final ImageIcon EJB_FILE_ICON = new ImageIcon(JarFileTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/ejbmodule_obj.gif\"));\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.jar\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        int lastSlashIndex = entry.getPath().lastIndexOf(\"/\");\n        String label = entry.getPath().substring(lastSlashIndex+1);\n        String location = new File(entry.getUri()).getPath();\n        ImageIcon icon = isAEjbModule(entry) ? EJB_FILE_ICON : JAR_FILE_ICON;\n        T node = (T)new TreeNode(entry, new TreeNodeBean(label, \"Location: \" + location, icon));\n        // Add dummy node\n        node.add(new DefaultMutableTreeNode());\n        return node;\n    }\n\n    protected static boolean isAEjbModule(Container.Entry entry) {\n        Collection<Container.Entry> children = entry.getChildren();\n\n        if (children != null) {\n            Container.Entry metaInf = null;\n\n            for (Container.Entry child : children) {\n                if (child.getPath().equals(\"META-INF\")) {\n                    metaInf = child;\n                    break;\n                }\n            }\n\n            if (metaInf != null) {\n                children = metaInf.getChildren();\n\n                for (Container.Entry child : children) {\n                    if (child.getPath().equals(\"META-INF/ejb-jar.xml\")) {\n                        return true;\n                    }\n                }\n            }\n        }\n\n        return false;\n    }\n\n    protected static class TreeNode extends ZipFileTreeNodeFactoryProvider.TreeNode {\n        public TreeNode(Container.Entry entry, Object userObject) {\n            super(entry, userObject);\n        }\n\n        @Override\n        public Collection<Container.Entry> getChildren() {\n            return JarContainerEntryUtil.removeInnerTypeEntries(entry.getChildren());\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/JavaFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.view.component.JavaFilePage;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.File;\n\npublic class JavaFileTreeNodeFactoryProvider extends AbstractTypeFileTreeNodeFactoryProvider {\n    protected static final ImageIcon JAVA_FILE_ICON = new ImageIcon(JavaFileTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/jcu_obj.png\"));\n    protected static final Factory FACTORY = new Factory();\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.java\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        int lastSlashIndex = entry.getPath().lastIndexOf('/');\n        String label = entry.getPath().substring(lastSlashIndex+1);\n        String location = new File(entry.getUri()).getPath();\n        return (T)new FileTreeNode(entry, new TreeNodeBean(label, \"Location: \" + location, JAVA_FILE_ICON), FACTORY);\n    }\n\n    protected static class Factory implements AbstractTypeFileTreeNodeFactoryProvider.PageAndTipFactory {\n        // --- PageAndTipFactory --- //\n        @Override\n        @SuppressWarnings(\"unchecked\")\n        public <T extends JComponent & UriGettable> T makePage(API a, Container.Entry e) {\n            return (T)new JavaFilePage(a, e);\n        }\n\n        @Override\n        public String makeTip(API api, Container.Entry entry) {\n            String location = new File(entry.getUri()).getPath();\n            StringBuilder tip = new StringBuilder(\"<html>Location: \");\n\n            tip.append(location);\n            tip.append(\"</html>\");\n\n            return tip.toString();\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/JavaModuleFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.File;\n\npublic class JavaModuleFileTreeNodeFactoryProvider extends ZipFileTreeNodeFactoryProvider {\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.jmod\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        int lastSlashIndex = entry.getPath().lastIndexOf(\"/\");\n        String label = entry.getPath().substring(lastSlashIndex+1);\n        String location = new File(entry.getUri()).getPath();\n        T node = (T)new TreeNode(entry, new TreeNodeBean(label, \"Location: \" + location, ICON));\n        // Add dummy node\n        node.add(new DefaultMutableTreeNode());\n        return node;\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/JavaModulePackageTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport java.util.regex.Pattern;\n\npublic class JavaModulePackageTreeNodeFactoryProvider extends PackageTreeNodeFactoryProvider {\n\n    @Override public String[] getSelectors() { return appendSelectors(\"jmod:dir:*\"); }\n\n    @Override\n    public Pattern getPathPattern() {\n        if (externalPathPattern == null) {\n            return Pattern.compile(\"classes\\\\/(?!META-INF)..*\");\n        } else {\n            return externalPathPattern;\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/JavascriptFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.fife.ui.rsyntaxtextarea.SyntaxConstants;\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.File;\n\npublic class JavascriptFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider {\n    protected static final ImageIcon ICON = new ImageIcon(JavascriptFileTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/js_obj.png\"));\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.js\"); }\n\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        int lastSlashIndex = entry.getPath().lastIndexOf(\"/\");\n        String label = entry.getPath().substring(lastSlashIndex+1);\n        String location = new File(entry.getUri()).getPath();\n        return (T)new TreeNode(entry, new TreeNodeBean(label, \"Location: \" + location, ICON));\n    }\n\n    protected static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode {\n        public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); }\n\n        // --- PageCreator --- //\n        @Override\n        @SuppressWarnings(\"unchecked\")\n        public <T extends JComponent & UriGettable> T createPage(API api) {\n            return (T)new TextFileTreeNodeFactoryProvider.Page(entry) {\n                @Override public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_JAVASCRIPT; }\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/JsonFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.fife.ui.rsyntaxtextarea.SyntaxConstants;\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.File;\n\npublic class JsonFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider {\n    protected static final ImageIcon ICON = new ImageIcon(JsonFileTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/ascii_obj.png\"));\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.json\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        int lastSlashIndex = entry.getPath().lastIndexOf(\"/\");\n        String label = entry.getPath().substring(lastSlashIndex+1);\n        String location = new File(entry.getUri()).getPath();\n        return (T)new TreeNode(entry, new TreeNodeBean(label, \"Location: \" + location, ICON));\n    }\n\n    protected static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode {\n        public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); }\n\n        // --- PageCreator --- //\n        @Override\n        @SuppressWarnings(\"unchecked\")\n        public <T extends JComponent & UriGettable> T createPage(API api) {\n            return (T)new TextFileTreeNodeFactoryProvider.Page(entry) {\n                @Override public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_JSON; }\n            };\n        }\n    }\n}"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/JspFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.fife.ui.rsyntaxtextarea.SyntaxConstants;\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.File;\n\npublic class JspFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider {\n    protected static final ImageIcon ICON = new ImageIcon(HtmlFileTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/html_obj.gif\"));\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.jsp\", \"*:file:*.jspf\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        int lastSlashIndex = entry.getPath().lastIndexOf(\"/\");\n        String label = entry.getPath().substring(lastSlashIndex+1);\n        String location = new File(entry.getUri()).getPath();\n        return (T)new TreeNode(entry, new TreeNodeBean(label, \"Location: \" + location, ICON));\n    }\n\n    protected static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode {\n        public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); }\n\n        // --- PageCreator --- //\n        @Override\n        @SuppressWarnings(\"unchecked\")\n        public <T extends JComponent & UriGettable> T createPage(API api) {\n            return (T)new TextFileTreeNodeFactoryProvider.Page(entry) {\n                @Override public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_JSP; }\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/KarFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.File;\n\npublic class KarFileTreeNodeFactoryProvider extends ZipFileTreeNodeFactoryProvider {\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.kar\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        int lastSlashIndex = entry.getPath().lastIndexOf(\"/\");\n        String label = entry.getPath().substring(lastSlashIndex+1);\n        String location = new File(entry.getUri()).getPath();\n        T node = (T)new TreeNode(entry, new TreeNodeBean(label, \"Location: \" + location, ICON));\n        // Add dummy node\n        node.add(new DefaultMutableTreeNode());\n        return node;\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/ManifestFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.PageCreator;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.view.component.ManifestFilePage;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.File;\n\npublic class ManifestFileTreeNodeFactoryProvider extends FileTreeNodeFactoryProvider {\n    protected static final ImageIcon ICON = new ImageIcon(ManifestFileTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/manifest_obj.png\"));\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:META-INF/MANIFEST.MF\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        String location = new File(entry.getUri()).getPath();\n        return (T)new TreeNode(entry, new TreeNodeBean(\"MANIFEST.MF\", \"Location: \" + location, ICON));\n    }\n\n    protected static class TreeNode extends FileTreeNodeFactoryProvider.TreeNode implements PageCreator {\n        public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); }\n\n        // --- PageCreator --- //\n        @Override\n        @SuppressWarnings(\"unchecked\")\n        public <T extends JComponent & UriGettable> T createPage(API api) {\n            return (T)new ManifestFilePage(api, entry);\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/MetainfDirectoryTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use, \n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport javax.swing.*;\nimport java.util.regex.Pattern;\n\npublic class MetainfDirectoryTreeNodeFactoryProvider extends DirectoryTreeNodeFactoryProvider {\n    protected static final ImageIcon ICON = new ImageIcon(MetainfDirectoryTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/inf_obj.png\"));\n\n    @Override public String[] getSelectors() {\n        return appendSelectors(\n                \"jar:dir:META-INF\",\n                \"war:dir:WEB-INF\",\n                \"war:dir:WEB-INF/classes/META-INF\",\n                \"ear:dir:META-INF\",\n                \"jmod:dir:classes/META-INF\");\n    }\n\n    @Override public ImageIcon getIcon() { return ICON; }\n    @Override public ImageIcon getOpenIcon() { return null; }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/MetainfServiceFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.PageCreator;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.view.component.OneTypeReferencePerLinePage;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.File;\nimport java.util.regex.Pattern;\n\npublic class MetainfServiceFileTreeNodeFactoryProvider extends FileTreeNodeFactoryProvider {\n    protected static final ImageIcon ICON = new ImageIcon(TextFileTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/ascii_obj.png\"));\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*\"); }\n\n    @Override\n    public Pattern getPathPattern() {\n        if (externalPathPattern == null) {\n            return Pattern.compile(\"META-INF\\\\/services\\\\/[^\\\\/]+\");\n        } else {\n            return externalPathPattern;\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        int lastSlashIndex = entry.getPath().lastIndexOf(\"/\");\n        String label = entry.getPath().substring(lastSlashIndex+1);\n        String location = new File(entry.getUri()).getPath();\n        return (T)new TreeNode(entry, new TreeNodeBean(label, \"Location: \" + location, ICON));\n    }\n\n    protected static class TreeNode extends FileTreeNodeFactoryProvider.TreeNode implements PageCreator {\n        public TreeNode(Container.Entry entry, Object userObject) {\n            super(entry, userObject);\n        }\n\n        // --- PageCreator --- //\n        @Override\n        @SuppressWarnings(\"unchecked\")\n        public <T extends JComponent & UriGettable> T createPage(API api) {\n            return (T)new OneTypeReferencePerLinePage(api, entry);\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/ModuleInfoFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Type;\nimport org.jd.gui.spi.TypeFactory;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.view.component.ModuleInfoFilePage;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.File;\nimport java.util.Collection;\nimport java.util.regex.Pattern;\n\npublic class ModuleInfoFileTreeNodeFactoryProvider extends ClassFileTreeNodeFactoryProvider {\n    protected static final ImageIcon MODULE_FILE_ICON = new ImageIcon(ClassFileTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/module_obj.png\"));\n    protected static final Factory FACTORY = new Factory();\n\n    static {\n        // Early class loading\n        try {\n            Class.forName(ModuleInfoFilePage.class.getName());\n        } catch (Exception e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n    }\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*/module-info.class\"); }\n\n    @Override public Pattern getPathPattern() { return externalPathPattern; }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        int lastSlashIndex = entry.getPath().lastIndexOf('/');\n        String label = entry.getPath().substring(lastSlashIndex+1);\n        return (T)new ModuleInfoFileTreeNode(entry, new TreeNodeBean(label, CLASS_FILE_ICON), FACTORY);\n    }\n\n    protected static class ModuleInfoFileTreeNode extends FileTreeNode {\n        public ModuleInfoFileTreeNode(Container.Entry entry, Object userObject, PageAndTipFactory pageAndTipFactory) {\n            super(entry, null, userObject, pageAndTipFactory);\n        }\n\n        // --- TreeNodeExpandable --- //\n        @Override\n        public void populateTreeNode(API api) {\n            if (!initialized) {\n                removeAllChildren();\n                // Create type node\n                TypeFactory typeFactory = api.getTypeFactory(entry);\n\n                if (typeFactory != null) {\n                    Collection<Type> types = typeFactory.make(api, entry);\n\n                    for (Type type : types) {\n                        add(new BaseTreeNode(entry, type.getName(), new TreeNodeBean(type.getDisplayTypeName(), MODULE_FILE_ICON), factory));\n                    }\n                }\n\n                initialized = true;\n            }\n        }\n    }\n\n    protected static class Factory implements AbstractTypeFileTreeNodeFactoryProvider.PageAndTipFactory {\n        // --- PageAndTipFactory --- //\n        @Override\n        @SuppressWarnings(\"unchecked\")\n        public <T extends JComponent & UriGettable> T makePage(API a, Container.Entry e) {\n            return (T)new ModuleInfoFilePage(a, e);\n        }\n\n        @Override\n        public String makeTip(API api, Container.Entry entry) {\n            String location = new File(entry.getUri()).getPath();\n            StringBuilder tip = new StringBuilder(\"<html>Location: \");\n\n            tip.append(location);\n            tip.append(\"</html>\");\n\n            return tip.toString();\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/PackageTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.util.container.JarContainerEntryUtil;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.File;\nimport java.util.Collection;\nimport java.util.regex.Pattern;\n\npublic class PackageTreeNodeFactoryProvider extends DirectoryTreeNodeFactoryProvider {\n    protected static final ImageIcon ICON = new ImageIcon(PackageTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/package_obj.png\"));\n\n    @Override public String[] getSelectors() { return appendSelectors(\"jar:dir:*\"); }\n\n    @Override\n    public Pattern getPathPattern() {\n        if (externalPathPattern == null) {\n            return Pattern.compile(\"(META-INF\\\\/versions\\\\/.*)|(?!META-INF)..*\");\n        } else {\n            return externalPathPattern;\n        }\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        int lastSlashIndex = entry.getPath().lastIndexOf(\"/\");\n        Collection<Container.Entry> entries = entry.getChildren();\n\n        // Aggregate directory names\n        while (entries.size() == 1) {\n            Container.Entry child = entries.iterator().next();\n            if (!child.isDirectory() || (api.getTreeNodeFactory(child) != this) || (entry.getContainer() != child.getContainer())) break;\n            entry = child;\n            entries = entry.getChildren();\n        }\n\n        String label = entry.getPath().substring(lastSlashIndex+1).replace(\"/\", \".\");\n        String location = new File(entry.getUri()).getPath();\n        T node = (T)new TreeNode(entry, new TreeNodeBean(label, \"Location: \" + location, getIcon(), getOpenIcon()));\n\n        if (entries.size() > 0) {\n            // Add dummy node\n            node.add(new DefaultMutableTreeNode());\n        }\n\n        return node;\n    }\n\n    @Override public ImageIcon getIcon() { return ICON; }\n    @Override public ImageIcon getOpenIcon() { return null; }\n\n    protected static class TreeNode extends DirectoryTreeNodeFactoryProvider.TreeNode {\n        public TreeNode(Container.Entry entry, Object userObject) {\n            super(entry, userObject);\n        }\n\n        @Override\n        public Collection<Container.Entry> getChildren() {\n            return JarContainerEntryUtil.removeInnerTypeEntries(entry.getChildren());\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/PropertiesFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use, \n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.fife.ui.rsyntaxtextarea.SyntaxConstants;\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.File;\n\npublic class PropertiesFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider {\n    protected static final ImageIcon ICON = new ImageIcon(PropertiesFileTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/ascii_obj.png\"));\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.properties\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        int lastSlashIndex = entry.getPath().lastIndexOf(\"/\");\n        String label = entry.getPath().substring(lastSlashIndex+1);\n        String location = new File(entry.getUri()).getPath();\n        return (T)new TreeNode(entry, new TreeNodeBean(label, \"Location: \" + location, ICON));\n    }\n\n    protected static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode {\n        public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); }\n\n        // --- PageCreator --- //\n        @Override\n        @SuppressWarnings(\"unchecked\")\n        public <T extends JComponent & UriGettable> T createPage(API api) {\n            return (T)new TextFileTreeNodeFactoryProvider.Page(entry) {\n                @Override public String getSyntaxStyle() {\n                    return SyntaxConstants.SYNTAX_STYLE_PROPERTIES_FILE;\n                }\n            };\n        }\n    }\n}"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/SpiFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport java.util.regex.Pattern;\n\npublic class SpiFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider {\n    @Override public String[] getSelectors() {\n        return appendSelectors(\"*:file:*\");\n    }\n\n    @Override\n    public Pattern getPathPattern() {\n        if (externalPathPattern == null) {\n            return Pattern.compile(\"(.*\\\\/)?META-INF\\\\/services\\\\/.*\");\n        } else {\n            return externalPathPattern;\n        }\n    }\n}"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/SqlFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.fife.ui.rsyntaxtextarea.SyntaxConstants;\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.File;\n\npublic class SqlFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider {\n    protected static final ImageIcon ICON = new ImageIcon(SqlFileTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/sql_obj.png\"));\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.sql\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        int lastSlashIndex = entry.getPath().lastIndexOf(\"/\");\n        String label = entry.getPath().substring(lastSlashIndex+1);\n        String location = new File(entry.getUri()).getPath();\n        return (T)new TreeNode(entry, new TreeNodeBean(label, \"Location: \" + location, ICON));\n    }\n\n    protected static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode {\n        public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); }\n\n        // --- PageCreator --- //\n        @Override\n        @SuppressWarnings(\"unchecked\")\n        public <T extends JComponent & UriGettable> T createPage(API api) {\n            return (T)new TextFileTreeNodeFactoryProvider.Page(entry) {\n                @Override public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_SQL; }\n            };\n        }\n    }\n}"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/TextFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;\nimport org.fife.ui.rsyntaxtextarea.Theme;\nimport org.fife.ui.rtextarea.Gutter;\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.PageCreator;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.util.io.TextReader;\nimport org.jd.gui.view.component.TextPage;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URI;\n\npublic class TextFileTreeNodeFactoryProvider extends FileTreeNodeFactoryProvider {\n    protected static final ImageIcon ICON = new ImageIcon(TextFileTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/ascii_obj.png\"));\n\n    static {\n        // Early class loading\n        new Gutter(new RSyntaxTextArea());\n        try {\n            Theme.load(TextFileTreeNodeFactoryProvider.class.getClassLoader().getResourceAsStream(\"rsyntaxtextarea/themes/eclipse.xml\"));\n        } catch (IOException e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n    }\n\n    @Override public String[] getSelectors() {\n        return appendSelectors(\"*:file:*.txt\", \"*:file:*.md\", \"*:file:*.SF\", \"*:file:*.policy\", \"*:file:*.yaml\", \"*:file:*.yml\", \"*:file:*/COPYRIGHT\", \"*:file:*/LICENSE\");\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        int lastSlashIndex = entry.getPath().lastIndexOf(\"/\");\n        String label = entry.getPath().substring(lastSlashIndex+1);\n        String location = new File(entry.getUri()).getPath();\n        return (T)new TreeNode(entry, new TreeNodeBean(label, \"Location: \" + location, ICON));\n    }\n\n    protected static class TreeNode extends FileTreeNodeFactoryProvider.TreeNode implements PageCreator {\n        public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); }\n\n        // --- PageCreator --- //\n        @Override\n        @SuppressWarnings(\"unchecked\")\n        public <T extends JComponent & UriGettable> T createPage(API api) {\n            return (T)new Page(entry);\n        }\n    }\n\n    protected static class Page extends TextPage implements UriGettable {\n        protected Container.Entry entry;\n\n        public Page(Container.Entry entry) {\n            this.entry = entry;\n            setText(TextReader.getText(entry.getInputStream()));\n        }\n\n        // --- UriGettable --- //\n        @Override public URI getUri() { return entry.getUri(); }\n\n        // --- ContentSavable --- //\n        public String getFileName() {\n            String path = entry.getPath();\n            int index = path.lastIndexOf(\"/\");\n            return path.substring(index+1);\n        }\n    }\n}"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/WarFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.File;\n\npublic class WarFileTreeNodeFactoryProvider extends ZipFileTreeNodeFactoryProvider {\n    protected static final ImageIcon ICON = new ImageIcon(JarFileTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/war_obj.gif\"));\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.war\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        int lastSlashIndex = entry.getPath().lastIndexOf(\"/\");\n        String label = entry.getPath().substring(lastSlashIndex+1);\n        String location = new File(entry.getUri()).getPath();\n        T node = (T)new TreeNode(entry, new TreeNodeBean(label, \"Location: \" + location, ICON));\n        // Add dummy node\n        node.add(new DefaultMutableTreeNode());\n        return node;\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/WarPackageTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport java.util.regex.Pattern;\n\npublic class WarPackageTreeNodeFactoryProvider extends PackageTreeNodeFactoryProvider {\n\n    @Override public String[] getSelectors() { return appendSelectors(\"war:dir:*\"); }\n\n    @Override\n    public Pattern getPathPattern() {\n        if (externalPathPattern == null) {\n            return Pattern.compile(\"WEB-INF\\\\/classes\\\\/(?!META-INF)..*\");\n        } else {\n            return externalPathPattern;\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/WebXmlFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.PageCreator;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.view.component.WebXmlFilePage;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.File;\n\npublic class WebXmlFileTreeNodeFactoryProvider extends FileTreeNodeFactoryProvider {\n    protected static final ImageIcon ICON = new ImageIcon(ManifestFileTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/xml_obj.gif\"));\n\n    @Override public String[] getSelectors() { return appendSelectors(\"war:file:WEB-INF/web.xml\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        String location = new File(entry.getUri()).getPath();\n        return (T)new TreeNode(entry, new TreeNodeBean(\"web.xml\", \"Location: \" + location, ICON));\n    }\n\n    protected static class TreeNode extends FileTreeNodeFactoryProvider.TreeNode implements PageCreator {\n        public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); }\n\n        // --- PageCreator --- //\n        @Override\n        @SuppressWarnings(\"unchecked\")\n        public <T extends JComponent & UriGettable> T createPage(API api) {\n            return (T)new WebXmlFilePage(api, entry);\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/WebinfLibDirectoryTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use, \n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport javax.swing.*;\n\npublic class WebinfLibDirectoryTreeNodeFactoryProvider extends DirectoryTreeNodeFactoryProvider {\n    protected static final ImageIcon ICON = new ImageIcon(WebinfLibDirectoryTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/archivefolder_obj.png\"));\n\n    @Override public String[] getSelectors() { return appendSelectors(\"war:dir:WEB-INF/lib\"); }\n    @Override public ImageIcon getIcon() { return ICON; }\n    @Override public ImageIcon getOpenIcon() { return null; }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/XmlBasedFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.fife.ui.rsyntaxtextarea.SyntaxConstants;\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.File;\n\npublic class XmlBasedFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider {\n    protected static final ImageIcon ICON = new ImageIcon(XmlBasedFileTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/xml_obj.gif\"));\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.xsl\", \"*:file:*.xslt\", \"*:file:*.xsd\", \"*:file:*.tld\", \"*:file:*.wsdl\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        int lastSlashIndex = entry.getPath().lastIndexOf(\"/\");\n        String label = entry.getPath().substring(lastSlashIndex+1);\n        String location = new File(entry.getUri()).getPath();\n        return (T)new TreeNode(entry, new TreeNodeBean(label, \"Location: \" + location, ICON));\n    }\n\n    static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode {\n        public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); }\n\n        // --- PageCreator --- //\n        @Override\n        @SuppressWarnings(\"unchecked\")\n        public <T extends JComponent & UriGettable> T createPage(API api) {\n            return (T)new TextFileTreeNodeFactoryProvider.Page(entry) {\n                @Override public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_XML; }\n            };\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/XmlFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.view.component.XmlFilePage;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.File;\n\npublic class XmlFileTreeNodeFactoryProvider extends TextFileTreeNodeFactoryProvider {\n    protected static final ImageIcon ICON = new ImageIcon(XmlFileTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/xml_obj.gif\"));\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.xml\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        int lastSlashIndex = entry.getPath().lastIndexOf(\"/\");\n        String label = entry.getPath().substring(lastSlashIndex+1);\n        String location = new File(entry.getUri()).getPath();\n        return (T)new TreeNode(entry, new TreeNodeBean(label, \"Location: \" + location, ICON));\n    }\n\n    protected static class TreeNode extends TextFileTreeNodeFactoryProvider.TreeNode {\n        public TreeNode(Container.Entry entry, Object userObject) { super(entry, userObject); }\n\n        // --- PageCreator --- //\n        @Override\n        @SuppressWarnings(\"unchecked\")\n        public <T extends JComponent & UriGettable> T createPage(API api) {\n            return (T)new XmlFilePage(api, entry);\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/treenode/ZipFileTreeNodeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.treenode;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContainerEntryGettable;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.spi.TreeNodeFactory;\nimport org.jd.gui.view.data.TreeNodeBean;\n\nimport javax.swing.*;\nimport javax.swing.tree.DefaultMutableTreeNode;\nimport java.io.File;\n\npublic class ZipFileTreeNodeFactoryProvider extends DirectoryTreeNodeFactoryProvider {\n    protected static final ImageIcon ICON = new ImageIcon(ZipFileTreeNodeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/zip_obj.png\"));\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.zip\", \"*:file:*.aar\"); }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public <T extends DefaultMutableTreeNode & ContainerEntryGettable & UriGettable> T make(API api, Container.Entry entry) {\n        int lastSlashIndex = entry.getPath().lastIndexOf(\"/\");\n        String label = entry.getPath().substring(lastSlashIndex+1);\n        String location = new File(entry.getUri()).getPath();\n        T node = (T)new TreeNode(entry, new TreeNodeBean(label, \"Location: \" + location, ICON));\n        // Add dummy node\n        node.add(new DefaultMutableTreeNode());\n        return node;\n    }\n\n    protected static class TreeNode extends DirectoryTreeNodeFactoryProvider.TreeNode {\n        public TreeNode(Container.Entry entry, Object userObject) {\n            super(entry, userObject);\n        }\n\n        // --- TreeNodeExpandable --- //\n        public void populateTreeNode(API api) {\n            if (!initialized) {\n                removeAllChildren();\n\n                for (Container.Entry e : getChildren()) {\n                    TreeNodeFactory factory = api.getTreeNodeFactory(e);\n                    if (factory != null) {\n                        add(factory.make(api, e));\n                    }\n                }\n\n                initialized = true;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/type/AbstractTypeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.type;\n\nimport org.jd.gui.api.model.Type;\nimport org.jd.gui.spi.TypeFactory;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.awt.image.BufferedImage;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.*;\nimport java.util.List;\nimport java.util.regex.Pattern;\n\npublic abstract class AbstractTypeFactoryProvider implements TypeFactory {\n    protected List<String> externalSelectors;\n    protected Pattern externalPathPattern;\n\n    /**\n     * Initialize \"selectors\" and \"pathPattern\" with optional external properties file\n     */\n    public AbstractTypeFactoryProvider() {\n        Properties properties = new Properties();\n        Class clazz = this.getClass();\n\n        try (InputStream is = clazz.getClassLoader().getResourceAsStream(clazz.getName().replace('.', '/') + \".properties\")) {\n            if (is != null) {\n                properties.load(is);\n            }\n        } catch (IOException e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n\n        init(properties);\n    }\n\n    protected void init(Properties properties) {\n        String selectors = properties.getProperty(\"selectors\");\n        externalSelectors = (selectors == null) ? null : Arrays.asList(selectors.split(\",\"));\n\n        String pathRegExp = properties.getProperty(\"pathRegExp\");\n        externalPathPattern = (pathRegExp == null) ? null : Pattern.compile(pathRegExp);\n    }\n\n    protected String[] appendSelectors(String selector) {\n        if (externalSelectors == null) {\n            return new String[] { selector };\n        } else {\n            int size = externalSelectors.size();\n            String[] array = new String[size+1];\n            externalSelectors.toArray(array);\n            array[size] = selector;\n            return array;\n        }\n    }\n\n    protected String[] appendSelectors(String... selectors) {\n        if (externalSelectors == null) {\n            return selectors;\n        } else {\n            int size = externalSelectors.size();\n            String[] array = new String[size+selectors.length];\n            externalSelectors.toArray(array);\n            System.arraycopy(selectors, 0, array, size, selectors.length);\n            return array;\n        }\n    }\n\n    public Pattern getPathPattern() { return externalPathPattern; }\n\n    // Signature writers\n    protected static int writeSignature(StringBuilder sb, String descriptor, int  length, int index, boolean varargsFlag) {\n        while (true) {\n            // Print array : '[[?' ou '[L[?;'\n            int dimensionLength = 0;\n\n            if (descriptor.charAt(index) == '[') {\n                dimensionLength++;\n\n                while (++index < length) {\n                    if ((descriptor.charAt(index) == 'L') && (index+1 < length) && (descriptor.charAt(index+1) == '[')) {\n                        index++;\n                        length--;\n                        dimensionLength++;\n                    } else if (descriptor.charAt(index) == '[') {\n                        dimensionLength++;\n                    } else {\n                        break;\n                    }\n                }\n            }\n\n            switch(descriptor.charAt(index)) {\n                case 'B': sb.append(\"byte\"); index++; break;\n                case 'C': sb.append(\"char\"); index++; break;\n                case 'D': sb.append(\"double\"); index++; break;\n                case 'F': sb.append(\"float\"); index++; break;\n                case 'I': sb.append(\"int\"); index++; break;\n                case 'J': sb.append(\"long\"); index++; break;\n                case 'L': case '.':\n                    int beginIndex = ++index;\n                    char c = '.';\n\n                    // Search ; or de <\n                    while (index < length) {\n                        c = descriptor.charAt(index);\n                        if ((c == ';') || (c == '<'))\n                            break;\n                        index++;\n                    }\n\n                    String internalClassName = descriptor.substring(beginIndex, index);\n                    int lastPackageSeparatorIndex = internalClassName.lastIndexOf('/');\n\n                    if (lastPackageSeparatorIndex >= 0) {\n                        // Cut package name\n                        internalClassName = internalClassName.substring(lastPackageSeparatorIndex + 1);\n                    }\n\n                    sb.append(internalClassName.replace('$', '.'));\n\n                    if (c == '<') {\n                        sb.append('<');\n                        index = writeSignature(sb, descriptor, length, index+1, false);\n\n                        while (descriptor.charAt(index) != '>') {\n                            sb.append(\", \");\n                            index = writeSignature(sb, descriptor, length, index, false);\n                        }\n                        sb.append('>');\n\n                        // pass '>'\n                        index++;\n                    }\n\n                    // pass ';'\n                    if (descriptor.charAt(index) == ';')\n                        index++;\n                    break;\n                case 'S': sb.append(\"short\"); index++; break;\n                case 'T':\n                    beginIndex = ++index;\n                    index = descriptor.substring(beginIndex, length).indexOf(';');\n                    sb.append(descriptor.substring(beginIndex, index));\n                    index++;\n                    break;\n                case 'V': sb.append(\"void\"); index++; break;\n                case 'Z': sb.append(\"boolean\"); index++; break;\n                case '-':\n                    sb.append(\"? super \");\n                    index = writeSignature(sb, descriptor, length, index+1, false);\n                    break;\n                case '+':\n                    sb.append(\"? extends \");\n                    index = writeSignature(sb, descriptor, length, index+1, false);\n                    break;\n                case '*': sb.append('?'); index++; break;\n                case 'X': case 'Y': sb.append(\"int\"); index++; break;\n                default:\n                    throw new RuntimeException(\"SignatureWriter.WriteSignature: invalid signature '\" + descriptor + \"'\");\n            }\n\n            if (varargsFlag) {\n                if (dimensionLength > 0) {\n                    while (--dimensionLength > 0)\n                        sb.append(\"[]\");\n                    sb.append(\"...\");\n                }\n            } else {\n                while (dimensionLength-- > 0)\n                    sb.append(\"[]\");\n            }\n\n            if ((index >= length) || (descriptor.charAt(index) != '.'))\n                break;\n\n            sb.append('.');\n        }\n\n        return index;\n    }\n\n    protected static void writeMethodSignature(\n            StringBuilder sb, int typeAccess, int methodAccess, boolean isInnerClass,\n            String constructorName, String methodName, String descriptor) {\n        if (methodName.equals(\"<clinit>\")) {\n            sb.append(\"{...}\");\n        } else {\n            boolean isAConstructor = methodName.equals(\"<init>\");\n\n            if (isAConstructor) {\n                sb.append(constructorName);\n            } else {\n                sb.append(methodName);\n            }\n\n            // Skip generics\n            int length = descriptor.length();\n            int index = 0;\n\n            while ((index < length) && (descriptor.charAt(index) != '('))\n                index++;\n\n            if (descriptor.charAt(index) != '(') {\n                throw new RuntimeException(\"Signature format exception: '\" + descriptor + \"'\");\n            }\n\n            sb.append('(');\n\n            // pass '('\n            index++;\n\n            if (descriptor.charAt(index) != ')') {\n                if (isAConstructor && isInnerClass && ((typeAccess & Type.FLAG_STATIC) == 0)) {\n                    // Skip first parameter\n                    int lengthBackup = sb.length();\n                    index = writeSignature(sb, descriptor, length, index, false);\n                    sb.setLength(lengthBackup);\n                }\n\n                if (descriptor.charAt(index) != ')') {\n                    int varargsParameterIndex;\n\n                    if ((methodAccess & Type.FLAG_VARARGS) == 0) {\n                        varargsParameterIndex = Integer.MAX_VALUE;\n                    } else {\n                        // Count parameters\n                        int indexBackup = index;\n                        int lengthBackup = sb.length();\n\n                        varargsParameterIndex = -1;\n\n                        while (descriptor.charAt(index) != ')') {\n                            index = writeSignature(sb, descriptor, length, index, false);\n                            varargsParameterIndex++;\n                        }\n\n                        index = indexBackup;\n                        sb.setLength(lengthBackup);\n                    }\n\n                    // Write parameters\n                    index = writeSignature(sb, descriptor, length, index, false);\n\n                    int parameterIndex = 1;\n\n                    while (descriptor.charAt(index) != ')') {\n                        sb.append(\", \");\n                        index = writeSignature(sb, descriptor, length, index, (parameterIndex == varargsParameterIndex));\n                        parameterIndex++;\n                    }\n                }\n            }\n\n            if (isAConstructor) {\n                sb.append(')');\n            } else {\n                sb.append(\") : \");\n                writeSignature(sb, descriptor, length, ++index, false);\n            }\n        }\n    }\n\n    // Icon getters\n    protected static ImageIcon getTypeIcon(int access) {\n        if ((access & Type.FLAG_ANNOTATION) != 0)\n            return ANNOTATION_ICON;\n        else if ((access & Type.FLAG_INTERFACE) != 0)\n            return INTERFACE_ICONS[accessToIndex(access)];\n        else if ((access & Type.FLAG_ENUM) != 0)\n            return ENUM_ICON;\n        else\n            return CLASS_ICONS[accessToIndex(access)];\n    }\n\n    protected static ImageIcon getFieldIcon(int access) {\n        return FIELD_ICONS[accessToIndex(access)];\n    }\n\n    protected static ImageIcon getMethodIcon(int access) {\n        return METHOD_ICONS[accessToIndex(access)];\n    }\n\n    protected static int accessToIndex(int access) {\n        int index = 0;\n\n        if ((access & Type.FLAG_STATIC) != 0)\n            index += 4;\n\n        if ((access & Type.FLAG_FINAL) != 0)\n            index += 8;\n\n        if ((access & Type.FLAG_ABSTRACT) != 0)\n            index += 16;\n\n        if ((access & Type.FLAG_PUBLIC) != 0)\n            return index + 1;\n        else if ((access & Type.FLAG_PROTECTED) != 0)\n            return index + 2;\n        else if ((access & Type.FLAG_PRIVATE) != 0)\n            return index + 3;\n        else\n            return index;\n    }\n\n    // Internal graphic stuff ...\n    protected static ImageIcon mergeIcons(ImageIcon background, ImageIcon overlay, int x, int y) {\n        int w = background.getIconWidth();\n        int h = background.getIconHeight();\n        BufferedImage image = new BufferedImage(w, h,  BufferedImage.TYPE_INT_ARGB);\n\n        if (x + overlay.getIconWidth() > w)\n            x = w - overlay.getIconWidth();\n        if (y + overlay.getIconHeight() > h)\n            y = h - overlay.getIconHeight();\n\n        Graphics2D g2 = image.createGraphics();\n        g2.drawImage(background.getImage(), 0, 0, null);\n        g2.drawImage(overlay.getImage(), x, y, null);\n        g2.dispose();\n\n        return new ImageIcon(image);\n    }\n\n    protected static ImageIcon[] mergeIcons(ImageIcon[] backgrounds, ImageIcon overlay, int x, int y) {\n        int length = backgrounds.length;\n        ImageIcon[] result = new ImageIcon[length*2];\n\n        // Copy source icons\n        System.arraycopy(backgrounds, 0, result, 0, length);\n\n        // Add overlays\n        for (int i=0; i<length; i++) {\n            result[length+i] = mergeIcons(backgrounds[i], overlay, x, y);\n        }\n\n        return result;\n    }\n\n    protected static final ImageIcon ABSTRACT_OVERLAY_ICON = new ImageIcon(ClassFileTypeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/abstract_ovr.png\"));\n    protected static final ImageIcon FINAL_OVERLAY_ICON = new ImageIcon(ClassFileTypeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/final_ovr.png\"));\n    protected static final ImageIcon STATIC_OVERLAY_ICON = new ImageIcon(ClassFileTypeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/static_ovr.png\"));\n\n    protected static final ImageIcon CLASS_ICON = new ImageIcon(ClassFileTypeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/class_default_obj.png\"));\n    protected static final ImageIcon PUBLIC_CLASS_ICON = new ImageIcon(ClassFileTypeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/class_obj.png\"));\n    protected static final ImageIcon PROTECTED_CLASS_ICON = new ImageIcon(ClassFileTypeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/class_protected_obj.png\"));\n    protected static final ImageIcon PRIVATE_CLASS_ICON = new ImageIcon(ClassFileTypeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/class_private_obj.png\"));\n\n    protected static final ImageIcon INTERFACE_ICON = new ImageIcon(ClassFileTypeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/int_default_obj.png\"));\n    protected static final ImageIcon PUBLIC_INTERFACE_ICON = new ImageIcon(ClassFileTypeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/int_obj.png\"));\n    protected static final ImageIcon PROTECTED_INTERFACE_ICON = new ImageIcon(ClassFileTypeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/int_protected_obj.png\"));\n    protected static final ImageIcon PRIVATE_INTERFACE_ICON = new ImageIcon(ClassFileTypeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/int_private_obj.png\"));\n\n    protected static final ImageIcon ANNOTATION_ICON = new ImageIcon(ClassFileTypeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/annotation_obj.png\"));\n    protected static final ImageIcon ENUM_ICON = new ImageIcon(ClassFileTypeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/enum_obj.png\"));\n\n    protected static final ImageIcon FIELD_ICON = new ImageIcon(ClassFileTypeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/field_default_obj.png\"));\n    protected static final ImageIcon PUBLIC_FIELD_ICON = new ImageIcon(ClassFileTypeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/field_public_obj.png\"));\n    protected static final ImageIcon PROTECTED_FIELD_ICON = new ImageIcon(ClassFileTypeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/field_protected_obj.png\"));\n    protected static final ImageIcon PRIVATE_FIELD_ICON = new ImageIcon(ClassFileTypeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/field_private_obj.png\"));\n\n    protected static final ImageIcon METHOD_ICON = new ImageIcon(ClassFileTypeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/methdef_obj.png\"));\n    protected static final ImageIcon PUBLIC_METHOD_ICON = new ImageIcon(ClassFileTypeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/methpub_obj.png\"));\n    protected static final ImageIcon PROTECTED_METHOD_ICON = new ImageIcon(ClassFileTypeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/methpro_obj.png\"));\n    protected static final ImageIcon PRIVATE_METHOD_ICON = new ImageIcon(ClassFileTypeFactoryProvider.class.getClassLoader().getResource(\"org/jd/gui/images/methpri_obj.png\"));\n\n    // Default icon set\n    protected static final ImageIcon[] DEFAULT_CLASS_ICONS = {\n            CLASS_ICON,\n            PUBLIC_CLASS_ICON,\n            PROTECTED_CLASS_ICON,\n            PRIVATE_CLASS_ICON\n    };\n\n    protected static final ImageIcon[] DEFAULT_INTERFACE_ICONS = {\n            INTERFACE_ICON,\n            PUBLIC_INTERFACE_ICON,\n            PROTECTED_INTERFACE_ICON,\n            PRIVATE_INTERFACE_ICON\n    };\n\n    protected static final ImageIcon[] DEFAULT_FIELD_ICONS = {\n            FIELD_ICON,\n            PUBLIC_FIELD_ICON,\n            PROTECTED_FIELD_ICON,\n            PRIVATE_FIELD_ICON\n    };\n\n    protected static final ImageIcon[] DEFAULT_METHOD_ICONS = {\n            METHOD_ICON,\n            PUBLIC_METHOD_ICON,\n            PROTECTED_METHOD_ICON,\n            PRIVATE_METHOD_ICON\n    };\n\n    // Add static icon set\n    protected static final ImageIcon[] STATIC_CLASS_ICONS = mergeIcons(DEFAULT_CLASS_ICONS, STATIC_OVERLAY_ICON, 100, 0);\n    protected static final ImageIcon[] STATIC_INTERFACE_ICONS = mergeIcons(DEFAULT_INTERFACE_ICONS, STATIC_OVERLAY_ICON, 100, 0);\n    protected static final ImageIcon[] STATIC_FIELD_ICONS = mergeIcons(DEFAULT_FIELD_ICONS, STATIC_OVERLAY_ICON, 100, 0);\n    protected static final ImageIcon[] STATIC_METHOD_ICONS = mergeIcons(DEFAULT_METHOD_ICONS, STATIC_OVERLAY_ICON, 100, 0);\n\n    // Add final icon set\n    protected static final ImageIcon[] FINAL_STATIC_CLASS_ICONS = mergeIcons(STATIC_CLASS_ICONS, FINAL_OVERLAY_ICON, 0, 0);\n    protected static final ImageIcon[] FINAL_STATIC_INTERFACE_ICONS = mergeIcons(STATIC_INTERFACE_ICONS, FINAL_OVERLAY_ICON, 0, 0);\n    protected static final ImageIcon[] FINAL_STATIC_FIELD_ICONS = mergeIcons(STATIC_FIELD_ICONS, FINAL_OVERLAY_ICON, 0, 0);\n    protected static final ImageIcon[] FINAL_STATIC_METHOD_ICONS = mergeIcons(STATIC_METHOD_ICONS, FINAL_OVERLAY_ICON, 0, 0);\n\n    // Add abstract icon set\n    protected static final ImageIcon[] CLASS_ICONS = mergeIcons(FINAL_STATIC_CLASS_ICONS, ABSTRACT_OVERLAY_ICON, 0, 100);\n    protected static final ImageIcon[] INTERFACE_ICONS = mergeIcons(FINAL_STATIC_INTERFACE_ICONS, ABSTRACT_OVERLAY_ICON, 0, 100);\n    protected static final ImageIcon[] FIELD_ICONS = mergeIcons(FINAL_STATIC_FIELD_ICONS, ABSTRACT_OVERLAY_ICON, 0, 100);\n    protected static final ImageIcon[] METHOD_ICONS = mergeIcons(FINAL_STATIC_METHOD_ICONS, ABSTRACT_OVERLAY_ICON, 0, 100);\n\n    // Cache\n    protected static class Cache<K, V> extends LinkedHashMap<K, V> {\n        public static final int CACHE_MAX_ENTRIES = 100;\n\n        public Cache() {\n            super(CACHE_MAX_ENTRIES*3/2, 0.7f, true);\n        }\n\n        @Override\n        protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {\n            return size() > CACHE_MAX_ENTRIES;\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/type/ClassFileTypeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.type;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Type;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.objectweb.asm.*;\n\nimport javax.swing.*;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URI;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\n\npublic class ClassFileTypeFactoryProvider extends AbstractTypeFactoryProvider {\n\n    static {\n        // Early class loading\n        try {\n            Class.forName(JavaType.class.getName());\n        } catch (Exception e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n    }\n\n    // Create cache\n    protected Cache<URI, JavaType> cache = new Cache<>();\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.class\"); }\n\n    @Override\n    public Collection<Type> make(API api, Container.Entry entry) {\n        return Collections.singletonList(make(api, entry, null));\n    }\n\n    @Override\n    public Type make(API api, Container.Entry entry, String fragment) {\n        URI key = entry.getUri();\n\n        if (cache.containsKey(key)) {\n            return cache.get(key);\n        } else {\n            JavaType type;\n\n            try (InputStream is = entry.getInputStream()) {\n                ClassReader classReader = new ClassReader(is);\n\n                if ((fragment != null) && (fragment.length() > 0)) {\n                    // Search type name in fragment. URI format : see jd.gui.api.feature.UriOpener\n                    int index = fragment.indexOf('-');\n                    if (index != -1) {\n                        // Keep type name only\n                        fragment = fragment.substring(0, index);\n                    }\n\n                    if (!classReader.getClassName().equals(fragment)) {\n                        // Search entry for type name\n                        String entryTypePath = classReader.getClassName() + \".class\";\n                        String fragmentTypePath = fragment + \".class\";\n\n                        while (true) {\n                            if (entry.getPath().endsWith(entryTypePath)) {\n                                // Entry path ends with the internal class name\n                                String pathToFound = entry.getPath().substring(0, entry.getPath().length() - entryTypePath.length()) + fragmentTypePath;\n                                Container.Entry entryFound = null;\n\n                                for (Container.Entry e : entry.getParent().getChildren()) {\n                                    if (e.getPath().equals(pathToFound)) {\n                                        entryFound = e;\n                                        break;\n                                    }\n                                }\n\n                                if (entryFound == null)\n                                    return null;\n\n                                entry = entryFound;\n\n                                try (InputStream is2 = entry.getInputStream()) {\n                                    classReader = new ClassReader(is2);\n                                } catch (IOException e) {\n                                    assert ExceptionUtil.printStackTrace(e);\n                                    return null;\n                                }\n                                break;\n                            }\n\n                            // Truncated path ? Cut first package name and retry\n                            int firstPackageSeparatorIndex = entryTypePath.indexOf('/');\n                            if (firstPackageSeparatorIndex == -1) {\n                                // Nothing to cut -> Stop\n                                return null;\n                            }\n\n                            entryTypePath = entryTypePath.substring(firstPackageSeparatorIndex + 1);\n                            fragmentTypePath = fragmentTypePath.substring(fragmentTypePath.indexOf('/') + 1);\n                        }\n                    }\n                }\n\n                type = new JavaType(entry, classReader, -1);\n            } catch (IOException e) {\n                assert ExceptionUtil.printStackTrace(e);\n                type = null;\n            }\n\n            cache.put(key, type);\n            return type;\n        }\n    }\n\n    static class JavaType implements Type {\n        protected Container.Entry entry;\n        protected int access;\n        protected String name;\n        protected String superName;\n        protected String outerName;\n\n        protected String displayTypeName;\n        protected String displayInnerTypeName;\n        protected String displayPackageName;\n\n        protected List<Type> innerTypes;\n        protected List<Type.Field> fields = new ArrayList<>();\n        protected List<Type.Method> methods = new ArrayList<>();\n\n        @SuppressWarnings(\"unchecked\")\n        protected JavaType(Container.Entry entry, ClassReader classReader, final int outerAccess) {\n            this.entry = entry;\n\n            ClassVisitor classAndInnerClassesVisitor = new ClassVisitor(Opcodes.ASM7) {\n                @Override\n                public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n                    JavaType.this.access = (outerAccess == -1) ? access : outerAccess;\n                    JavaType.this.name = name;\n                    JavaType.this.superName = ((access & Opcodes.ACC_INTERFACE) != 0) && \"java/lang/Object\".equals(superName) ? null : superName;\n                }\n\n                @Override\n                public void visitInnerClass(String name, String outerName, String innerName, int access) {\n                    if (JavaType.this.name.equals(name)) {\n                        // Inner class path found\n                        JavaType.this.outerName = outerName;\n                        JavaType.this.displayInnerTypeName = innerName;\n                    } else if (((access & (Opcodes.ACC_SYNTHETIC|Opcodes.ACC_BRIDGE)) == 0) && JavaType.this.name.equals(outerName)) {\n                        Container.Entry innerEntry = getEntry(name);\n\n                        if (innerEntry != null) {\n                            try (InputStream is = innerEntry.getInputStream()) {\n                                ClassReader classReader = new ClassReader(is);\n                                if (innerTypes == null) {\n                                    innerTypes = new ArrayList<>();\n                                }\n                                innerTypes.add(new JavaType(innerEntry, classReader, access));\n                            } catch (IOException e) {\n                                assert ExceptionUtil.printStackTrace(e);\n                            }\n                        }\n                    }\n                }\n            };\n\n            classReader.accept(classAndInnerClassesVisitor, ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES);\n\n            int lastPackageSeparatorIndex = name.lastIndexOf('/');\n\n            if (lastPackageSeparatorIndex == -1) {\n                displayPackageName = \"\";\n\n                if (outerName == null) {\n                    displayTypeName = name;\n                } else {\n                    displayTypeName = getDisplayTypeName(outerName, 0) + '.' + displayInnerTypeName;\n                }\n            } else {\n                displayPackageName = name.substring(0, lastPackageSeparatorIndex).replace('/', '.');\n\n                if (outerName == null) {\n                    displayTypeName = name;\n                } else {\n                    displayTypeName = getDisplayTypeName(outerName, lastPackageSeparatorIndex) + '.' + displayInnerTypeName;\n                }\n\n                displayTypeName = displayTypeName.substring(lastPackageSeparatorIndex+1);\n            }\n\n            ClassVisitor fieldsAndMethodsVisitor = new ClassVisitor(Opcodes.ASM7) {\n                @Override\n                public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {\n                    if ((access & (Opcodes.ACC_SYNTHETIC|Opcodes.ACC_ENUM)) == 0) {\n                        fields.add(new Type.Field() {\n                            public int getFlags() { return access; }\n                            public String getName() { return name; }\n                            public String getDescriptor() { return descriptor; }\n                            public Icon getIcon() { return getFieldIcon(access); }\n\n                            public String getDisplayName() {\n                                StringBuilder sb = new StringBuilder();\n                                sb.append(name).append(\" : \");\n                                writeSignature(sb, descriptor, descriptor.length(), 0, false);\n                                return sb.toString();\n                            }\n                        });\n                    }\n                    return null;\n                }\n\n                @Override\n                public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n                    if ((access & (Opcodes.ACC_SYNTHETIC|Opcodes.ACC_ENUM|Opcodes.ACC_BRIDGE)) == 0) {\n                        methods.add(new Type.Method() {\n                            public int getFlags() { return access; }\n                            public String getName() { return name; }\n                            public String getDescriptor() { return descriptor; }\n                            public Icon getIcon() { return getMethodIcon(access); }\n\n                            public String getDisplayName() {\n                                boolean isInnerClass = (JavaType.this.displayInnerTypeName != null);\n                                String constructorName = isInnerClass ? JavaType.this.displayInnerTypeName : JavaType.this.displayTypeName;\n                                StringBuilder sb = new StringBuilder();\n                                writeMethodSignature(sb, JavaType.this.access, access, isInnerClass, constructorName, name, descriptor);\n                                return sb.toString();\n                            }\n                        });\n                    }\n                    return null;\n                }\n            };\n\n            classReader.accept(fieldsAndMethodsVisitor, ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES);\n        }\n\n        @SuppressWarnings(\"unchecked\")\n        protected String getDisplayTypeName(String name, int packageLength) {\n            int indexDollar = name.lastIndexOf('$');\n\n            if (indexDollar > packageLength) {\n                Container.Entry entry = getEntry(name);\n\n                if (entry != null) {\n                    try (InputStream is = entry.getInputStream()) {\n                        ClassReader classReader = new ClassReader(is);\n                        InnerClassVisitor classVisitor = new InnerClassVisitor(name);\n\n                        classReader.accept(classVisitor, ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES);\n\n                        String outerName = classVisitor.getOuterName();\n\n                        if (outerName != null) {\n                            // Inner class path found => Recursive call\n                            return getDisplayTypeName(outerName, packageLength) + '.' + classVisitor.getInnerName();\n                        }\n                    } catch (IOException e) {\n                        assert ExceptionUtil.printStackTrace(e);\n                    }\n                }\n            }\n\n            return name;\n        }\n\n        protected Container.Entry getEntry(String typeName) {\n            String pathToFound = typeName + \".class\";\n\n            for (Container.Entry entry : entry.getParent().getChildren()) {\n                if (entry.getPath().equals(pathToFound)) {\n                    return entry;\n                }\n            }\n\n            return null;\n        }\n\n        @Override public int getFlags() { return access; }\n        @Override public String getName() { return name; }\n        @Override public String getSuperName() { return superName; }\n        @Override public String getOuterName() { return outerName; }\n        @Override public String getDisplayPackageName() { return displayPackageName; }\n        @Override public String getDisplayTypeName() { return displayTypeName; }\n        @Override public String getDisplayInnerTypeName() { return displayInnerTypeName; }\n        @Override public Icon getIcon() { return getTypeIcon(access); }\n        @Override public List<Type> getInnerTypes() { return innerTypes; }\n        @Override public List<Type.Field> getFields() { return fields; }\n        @Override public List<Type.Method> getMethods() { return methods; }\n    }\n\n    protected static class InnerClassVisitor extends ClassVisitor {\n        protected String name;\n        protected String outerName;\n        protected String innerName;\n\n        public InnerClassVisitor(String name) {\n            super(Opcodes.ASM7);\n            this.name = name;\n        }\n\n        @Override\n        public void visitInnerClass(String name, String outerName, String innerName, int access) {\n            if (this.name.equals(name)) {\n                // Inner class path found\n                this.outerName = outerName;\n                this.innerName = innerName;\n            }\n        }\n\n        public String getOuterName() {\n            return outerName;\n        }\n\n        public String getInnerName() {\n            return innerName;\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/type/JavaFileTypeFactoryProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.type;\n\nimport org.antlr.v4.runtime.ANTLRInputStream;\nimport org.antlr.v4.runtime.ParserRuleContext;\nimport org.antlr.v4.runtime.tree.ParseTree;\nimport org.antlr.v4.runtime.tree.TerminalNode;\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Type;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.util.parser.antlr.ANTLRJavaParser;\nimport org.jd.gui.util.parser.antlr.AbstractJavaListener;\nimport org.jd.gui.util.parser.antlr.JavaParser;\n\nimport javax.swing.*;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URI;\nimport java.util.*;\n\npublic class JavaFileTypeFactoryProvider extends AbstractTypeFactoryProvider {\n\n    static {\n        // Early class loading\n        ANTLRJavaParser.parse(new ANTLRInputStream(\"class EarlyLoading{}\"), new Listener(null));\n    }\n\n    // Create cache\n    protected Cache<URI, Listener> cache = new Cache<>();\n\n    @Override public String[] getSelectors() { return appendSelectors(\"*:file:*.java\"); }\n\n    @Override\n    public Collection<Type> make(API api, Container.Entry entry) {\n        Listener listener = getListener(entry);\n\n        if (listener == null) {\n            return Collections.emptyList();\n        } else {\n            return listener.getRootTypes();\n        }\n    }\n\n    @Override\n    public Type make(API api, Container.Entry entry, String fragment) {\n        Listener listener = getListener(entry);\n\n        if (listener == null) {\n            return null;\n        } else {\n            if ((fragment != null) && (fragment.length() > 0)) {\n                // Search type name in fragment. URI format : see jd.gui.api.feature.UriOpener\n                int index = fragment.indexOf('-');\n\n                if (index != -1) {\n                    // Keep type name only\n                    fragment = fragment.substring(0, index);\n                }\n\n                return listener.getType(fragment);\n            } else {\n                return listener.getMainType();\n            }\n        }\n    }\n\n    protected Listener getListener(Container.Entry entry) {\n        URI key = entry.getUri();\n\n        if (cache.containsKey(key)) {\n            return cache.get(key);\n        } else {\n            Listener listener;\n\n            try (InputStream inputStream = entry.getInputStream()) {\n                ANTLRJavaParser.parse(new ANTLRInputStream(inputStream), listener = new Listener(entry));\n            } catch (IOException e) {\n                assert ExceptionUtil.printStackTrace(e);\n                listener = null;\n            }\n\n            cache.put(key, listener);\n            return listener;\n        }\n    }\n\n    protected static class JavaType implements Type {\n        protected int access;\n        protected String name;\n        protected String superName;\n        protected String outerName;\n\n        protected String displayTypeName;\n        protected String displayInnerTypeName;\n        protected String displayPackageName;\n\n        protected List<Type> innerTypes = new ArrayList<>();\n        protected List<Field> fields = new ArrayList<>();\n        protected List<Method> methods = new ArrayList<>();\n\n        protected JavaType outerType;\n\n        public JavaType(\n                int access, String name, String superName, String outerName,\n                String displayTypeName, String displayInnerTypeName, String displayPackageName,\n                JavaType outerType) {\n\n            this.access = access;\n            this.name = name;\n            this.superName = superName;\n            this.outerName = outerName;\n            this.displayTypeName = displayTypeName;\n            this.displayInnerTypeName = displayInnerTypeName;\n            this.displayPackageName = displayPackageName;\n            this.outerType = outerType;\n        }\n\n        public int getFlags() { return access; }\n        public String getName() { return name; }\n        public String getSuperName() { return superName; }\n        public String getOuterName() { return outerName; }\n        public String getDisplayTypeName() { return displayTypeName; }\n        public String getDisplayInnerTypeName() { return displayInnerTypeName; }\n        public String getDisplayPackageName() { return displayPackageName; }\n        public Icon getIcon() { return getTypeIcon(access); }\n        public JavaType getOuterType() { return outerType; }\n        public Collection<Type> getInnerTypes() { return innerTypes; }\n        public Collection<Field> getFields() { return fields; }\n        public Collection<Method> getMethods() { return methods; }\n    }\n\n    protected static class JavaField implements Type.Field {\n        protected int access;\n        protected String name;\n        protected String descriptor;\n\n        public JavaField(int access, String name, String descriptor) {\n            this.access = access;\n            this.name = name;\n            this.descriptor = descriptor;\n        }\n\n        public int getFlags() { return access; }\n        public String getName() { return name; }\n        public String getDescriptor() { return descriptor; }\n        public Icon getIcon() { return getFieldIcon(access); }\n\n        public String getDisplayName() {\n            StringBuilder sb = new StringBuilder();\n            sb.append(name).append(\" : \");\n            writeSignature(sb, descriptor, descriptor.length(), 0, false);\n            return sb.toString();\n        }\n    }\n\n    protected static class JavaMethod implements Type.Method {\n        protected JavaType type;\n        protected int access;\n        protected String name;\n        protected String descriptor;\n\n        public JavaMethod(JavaType type, int access, String name, String descriptor) {\n            this.type = type;\n            this.access = access;\n            this.name = name;\n            this.descriptor = descriptor;\n        }\n\n        public int getFlags() { return access; }\n        public String getName() { return name; }\n        public String getDescriptor() { return descriptor; }\n        public Icon getIcon() { return getMethodIcon(access); }\n\n        public String getDisplayName() {\n            String constructorName = type.getDisplayInnerTypeName();\n            boolean isInnerClass = (constructorName != null);\n\n            if (constructorName == null)\n                constructorName = type.getDisplayTypeName();\n\n            StringBuilder sb = new StringBuilder();\n            writeMethodSignature(sb, access, access, isInnerClass, constructorName, name, descriptor);\n            return sb.toString();\n        }\n    }\n\n    protected static class Listener extends AbstractJavaListener {\n\n        protected String displayPackageName = \"\";\n\n        protected JavaType mainType = null;\n        protected JavaType currentType = null;\n        protected ArrayList<Type> rootTypes = new ArrayList<>();\n        protected HashMap<String, Type> types = new HashMap<>();\n\n        public Listener(Container.Entry entry) {\n            super(entry);\n        }\n\n        public Type getMainType() {\n            return mainType;\n        }\n        public Type getType(String typeName) {\n            return types.get(typeName);\n        }\n        public ArrayList<Type> getRootTypes() {\n            return rootTypes;\n        }\n\n        // --- ANTLR Listener --- //\n\n        public void enterPackageDeclaration(JavaParser.PackageDeclarationContext ctx) {\n            super.enterPackageDeclaration(ctx);\n            displayPackageName = packageName.replace('/', '.');\n        }\n\n        public void enterClassDeclaration(JavaParser.ClassDeclarationContext ctx) { enterTypeDeclaration(ctx, 0); }\n        public void exitClassDeclaration(JavaParser.ClassDeclarationContext ctx) { exitTypeDeclaration(); }\n\n        public void enterEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { enterTypeDeclaration(ctx, JavaType.FLAG_ENUM); }\n        public void exitEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { exitTypeDeclaration(); }\n\n        public void enterInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { enterTypeDeclaration(ctx, JavaType.FLAG_INTERFACE); }\n        public void exitInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { exitTypeDeclaration(); }\n\n        public void enterAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { enterTypeDeclaration(ctx, JavaType.FLAG_ANNOTATION); }\n        public void exitAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { exitTypeDeclaration(); }\n\n        protected void enterTypeDeclaration(ParserRuleContext ctx, int access) {\n            String name = ctx.getToken(JavaParser.Identifier, 0).getText();\n\n            JavaParser.TypeContext superType = ctx.getRuleContext(JavaParser.TypeContext.class, 0);\n            String superQualifiedTypeName;\n\n            if (superType == null) {\n                superQualifiedTypeName = ((access & JavaType.FLAG_INTERFACE) == 0) ? \"java/lang/Object\" : \"\";\n            } else {\n                superQualifiedTypeName = resolveInternalTypeName(superType.classOrInterfaceType().Identifier());\n            }\n\n            ParserRuleContext parent = ctx.getParent();\n\n            if (parent instanceof JavaParser.TypeDeclarationContext)\n                access += getTypeDeclarationContextAccessFlag(parent);\n            else if (parent instanceof JavaParser.MemberDeclarationContext)\n                access += getMemberDeclarationContextAccessFlag(parent.getParent());\n\n            if (currentType == null) {\n                String internalTypeName = packageName.isEmpty() ? name : packageName + \"/\" + name;\n                String outerName = null;\n                String displayTypeName = name;\n                String displayInnerTypeName = null;\n\n                currentType = new JavaType(access, internalTypeName, superQualifiedTypeName, outerName, displayTypeName, displayInnerTypeName, displayPackageName, null);\n                types.put(internalTypeName, currentType);\n                rootTypes.add(currentType);\n                nameToInternalTypeName.put(name, internalTypeName);\n\n                if (mainType == null) {\n                    mainType = currentType;\n                } else {\n                    // Multi class definitions in the same file\n                    String path = entry.getPath();\n                    int index = path.lastIndexOf('/') + 1;\n\n                    if (path.substring(index).startsWith(name + '.')) {\n                        // Select the correct root type\n                        mainType = currentType;\n                    }\n                }\n            } else {\n                String internalTypeName = currentType.getName() + '$' + name;\n                String outerName = currentType.getName();\n                String displayTypeName = currentType.getDisplayTypeName() + '.' + name;\n                String displayInnerTypeName = name;\n                JavaType subType = new JavaType(access, internalTypeName, superQualifiedTypeName, outerName, displayTypeName, displayInnerTypeName, displayPackageName, currentType);\n\n                currentType.getInnerTypes().add(subType);\n                currentType = subType;\n                types.put(internalTypeName, currentType);\n                nameToInternalTypeName.put(name, internalTypeName);\n            }\n        }\n\n        protected void exitTypeDeclaration() {\n            currentType = currentType.getOuterType();\n        }\n\n        public void enterClassBodyDeclaration(JavaParser.ClassBodyDeclarationContext ctx) {\n            if (ctx.getChildCount() == 2) {\n                ParseTree first = ctx.getChild(0);\n\n                if (first instanceof TerminalNode) {\n                    if (((TerminalNode)first).getSymbol().getType() == JavaParser.STATIC) {\n                        currentType.getMethods().add(new JavaMethod(currentType, JavaType.FLAG_STATIC, \"<clinit>\", \"()V\"));\n                    }\n                }\n            }\n        }\n\n        public void enterConstDeclaration(JavaParser.ConstDeclarationContext ctx) {\n            JavaParser.TypeContext typeContext = ctx.type();\n            int access = getClassBodyDeclarationAccessFlag(ctx.getParent().getParent());\n\n            for (JavaParser.ConstantDeclaratorContext constantDeclaratorContext : ctx.constantDeclarator()) {\n                TerminalNode identifier = constantDeclaratorContext.Identifier();\n                String name = identifier.getText();\n                int dimensionOnVariable = countDimension(constantDeclaratorContext.children);\n                String descriptor = createDescriptor(typeContext, dimensionOnVariable);\n\n                currentType.getFields().add(new JavaField(access, name, descriptor));\n            }\n        }\n\n        public void enterFieldDeclaration(JavaParser.FieldDeclarationContext ctx) {\n            JavaParser.TypeContext typeContext = ctx.type();\n            int access = getClassBodyDeclarationAccessFlag(ctx.getParent().getParent());\n\n            for (JavaParser.VariableDeclaratorContext declaration : ctx.variableDeclarators().variableDeclarator()) {\n                JavaParser.VariableDeclaratorIdContext variableDeclaratorId = declaration.variableDeclaratorId();\n                TerminalNode identifier = variableDeclaratorId.Identifier();\n                String name = identifier.getText();\n                int dimensionOnVariable = countDimension(variableDeclaratorId.children);\n                String descriptor = createDescriptor(typeContext, dimensionOnVariable);\n\n                currentType.getFields().add(new JavaField(access, name, descriptor));\n            }\n        }\n\n        public void enterMethodDeclaration(JavaParser.MethodDeclarationContext ctx) {\n            enterMethodDeclaration(ctx, ctx.Identifier(), ctx.formalParameters(), ctx.type());\n        }\n\n        public void enterInterfaceMethodDeclaration(JavaParser.InterfaceMethodDeclarationContext ctx) {\n            enterMethodDeclaration(ctx, ctx.Identifier(), ctx.formalParameters(), ctx.type());\n        }\n\n        public void enterMethodDeclaration(\n                ParserRuleContext ctx, TerminalNode identifier,\n                JavaParser.FormalParametersContext formalParameters, JavaParser.TypeContext returnType) {\n\n            int access = getClassBodyDeclarationAccessFlag(ctx.getParent().getParent());\n            String name = identifier.getText();\n            String paramDescriptors = createParamDescriptors(formalParameters.formalParameterList());\n            String returnDescriptor = createDescriptor(returnType, 0);\n            String descriptor = paramDescriptors + returnDescriptor;\n\n            currentType.getMethods().add(new JavaMethod(currentType, access, name, descriptor));\n        }\n\n        public void enterConstructorDeclaration(JavaParser.ConstructorDeclarationContext ctx) {\n            int access = getClassBodyDeclarationAccessFlag(ctx.getParent().getParent());\n            String paramDescriptors = createParamDescriptors(ctx.formalParameters().formalParameterList());\n            String descriptor = paramDescriptors + \"V\";\n\n            currentType.getMethods().add(new JavaMethod(currentType, access, \"<init>\", descriptor));\n        }\n\n        protected String createParamDescriptors(JavaParser.FormalParameterListContext formalParameterList) {\n            StringBuilder paramDescriptors = null;\n\n            if (formalParameterList != null) {\n                List<JavaParser.FormalParameterContext> formalParameters = formalParameterList.formalParameter();\n                paramDescriptors = new StringBuilder(\"(\");\n\n                for (JavaParser.FormalParameterContext formalParameter : formalParameters) {\n                    int dimensionOnParameter = countDimension(formalParameter.variableDeclaratorId().children);\n                    paramDescriptors.append(createDescriptor(formalParameter.type(), dimensionOnParameter));\n                }\n            }\n\n            return (paramDescriptors == null) ? \"()\" : paramDescriptors.append(')').toString();\n        }\n\n        protected int getTypeDeclarationContextAccessFlag(ParserRuleContext ctx) {\n            int access = 0;\n\n            for (JavaParser.ClassOrInterfaceModifierContext coiModifierContext : ctx.getRuleContexts(JavaParser.ClassOrInterfaceModifierContext.class)) {\n                access += getAccessFlag(coiModifierContext);\n            }\n\n            return access;\n        }\n\n        protected int getMemberDeclarationContextAccessFlag(ParserRuleContext ctx) {\n            int access = 0;\n\n            for (JavaParser.ModifierContext modifierContext : ctx.getRuleContexts(JavaParser.ModifierContext.class)) {\n                JavaParser.ClassOrInterfaceModifierContext coiModifierContext = modifierContext.classOrInterfaceModifier();\n                if (coiModifierContext != null) {\n                    access += getAccessFlag(coiModifierContext);\n                }\n            }\n\n            return access;\n        }\n\n        protected int getClassBodyDeclarationAccessFlag(ParserRuleContext ctx) {\n            if ((currentType.access & JavaType.FLAG_INTERFACE) == 0) {\n                int access = 0;\n\n                for (JavaParser.ModifierContext modifierContext : ctx.getRuleContexts(JavaParser.ModifierContext.class)) {\n                    JavaParser.ClassOrInterfaceModifierContext coimc = modifierContext.classOrInterfaceModifier();\n\n                    if (coimc != null) {\n                        access += getAccessFlag(coimc);\n                    }\n                }\n\n                return access;\n            } else {\n                return JavaType.FLAG_PUBLIC;\n            }\n        }\n\n        protected int getAccessFlag(JavaParser.ClassOrInterfaceModifierContext ctx) {\n            if (ctx.getChildCount() == 1) {\n                ParseTree first = ctx.getChild(0);\n\n                if (first instanceof TerminalNode) {\n                    switch (((TerminalNode)first).getSymbol().getType()) {\n                        case JavaParser.STATIC:    return JavaType.FLAG_STATIC;\n                        case JavaParser.FINAL:     return JavaType.FLAG_FINAL;\n                        case JavaParser.ABSTRACT:  return JavaType.FLAG_ABSTRACT;\n                        case JavaParser.PUBLIC:    return JavaType.FLAG_PUBLIC;\n                        case JavaParser.PROTECTED: return JavaType.FLAG_PROTECTED;\n                        case JavaParser.PRIVATE:   return JavaType.FLAG_PRIVATE;\n                    }\n                }\n            }\n\n            return 0;\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/service/uriloader/FileUriLoaderProvider.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.service.uriloader;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.spi.FileLoader;\nimport org.jd.gui.spi.UriLoader;\n\nimport java.io.File;\nimport java.net.URI;\n\npublic class FileUriLoaderProvider implements UriLoader {\n    protected static final String[] SCHEMES = { \"file\" };\n\n    public String[] getSchemes() { return SCHEMES; }\n\n    public boolean accept(API api, URI uri) { return \"file\".equals(uri.getScheme()); }\n\n    public boolean load(API api, URI uri) {\n        File file = new File(uri.getPath());\n        FileLoader fileLoader = api.getFileLoader(file);\n\n        return (fileLoader != null) && fileLoader.load(api, file);\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/util/container/JarContainerEntryUtil.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.util.container;\n\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.model.container.ContainerEntryComparator;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.Opcodes;\n\nimport java.io.InputStream;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.List;\n\npublic class JarContainerEntryUtil {\n    public static Collection<Container.Entry> removeInnerTypeEntries(Collection<Container.Entry> entries) {\n        HashSet<String> potentialOuterTypePaths = new HashSet<>();\n        Collection<Container.Entry> filtredSubEntries;\n\n        for (Container.Entry e : entries) {\n            if (!e.isDirectory()) {\n                String p = e.getPath();\n\n                if (p.toLowerCase().endsWith(\".class\")) {\n                    int lastSeparatorIndex = p.lastIndexOf('/');\n                    int dollarIndex = p.substring(lastSeparatorIndex+1).indexOf('$');\n\n                    if (dollarIndex != -1) {\n                        potentialOuterTypePaths.add(p.substring(0, lastSeparatorIndex+1+dollarIndex) + \".class\");\n                    }\n                }\n            }\n        }\n\n        if (potentialOuterTypePaths.size() == 0) {\n            filtredSubEntries = entries;\n        } else {\n            HashSet<String> innerTypePaths = new HashSet<>();\n\n            for (Container.Entry e : entries) {\n                if (!e.isDirectory() && potentialOuterTypePaths.contains(e.getPath())) {\n                    populateInnerTypePaths(innerTypePaths, e);\n                }\n            }\n\n            filtredSubEntries = new ArrayList<>();\n\n            for (Container.Entry e : entries) {\n                if (!e.isDirectory()) {\n                    String p = e.getPath();\n\n                    if (p.toLowerCase().endsWith(\".class\")) {\n                        int indexDollar = p.lastIndexOf('$');\n\n                        if (indexDollar != -1) {\n                            int indexSeparator = p.lastIndexOf('/');\n\n                            if (indexDollar > indexSeparator) {\n                                if (innerTypePaths.contains(p)) {\n                                    // Inner class found -> Skip\n                                    continue;\n                                } else {\n                                    populateInnerTypePaths(innerTypePaths, e);\n\n                                    if (innerTypePaths.contains(p)) {\n                                        // Inner class found -> Skip\n                                        continue;\n                                    }\n                                }\n                            }\n                        }\n                    }\n                }\n                // Valid path\n                filtredSubEntries.add(e);\n            }\n        }\n\n        List<Container.Entry> list = new ArrayList<>(filtredSubEntries);\n        list.sort(ContainerEntryComparator.COMPARATOR);\n\n        return list;\n    }\n\n    protected static void populateInnerTypePaths(final HashSet<String> innerTypePaths, Container.Entry entry) {\n        try (InputStream is = entry.getInputStream()) {\n            ClassReader classReader = new ClassReader(is);\n            String p = entry.getPath();\n            final String prefixPath = p.substring(0, p.length() - classReader.getClassName().length() - 6);\n\n            ClassVisitor classVisitor = new ClassVisitor(Opcodes.ASM7) {\n                public void visitInnerClass(final String name, final String outerName, final String innerName, final int access) {\n                    innerTypePaths.add(prefixPath + name + \".class\");\n                }\n            };\n\n            classReader.accept(classVisitor, ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES);\n        } catch (Exception e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/util/decompiler/ClassPathLoader.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.util.decompiler;\n\nimport org.jd.core.v1.api.loader.Loader;\nimport org.jd.core.v1.api.loader.LoaderException;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\n\npublic class ClassPathLoader implements Loader {\n    protected byte[] buffer = new byte[1024 * 4];\n\n    @Override\n    public boolean canLoad(String internalName) {\n        return this.getClass().getResource(\"/\" + internalName + \".class\") != null;\n    }\n\n    @Override\n    public byte[] load(String internalName) throws LoaderException {\n        InputStream is = this.getClass().getResourceAsStream(\"/\" + internalName + \".class\");\n\n        if (is == null) {\n            return null;\n        } else {\n            try (InputStream input=is; ByteArrayOutputStream output=new ByteArrayOutputStream()) {\n                int len = input.read(buffer);\n\n                while (len > 0) {\n                    output.write(buffer, 0, len);\n                    len = input.read(buffer);\n                }\n\n                return output.toByteArray();\n            } catch (IOException e) {\n                throw new LoaderException(e);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/util/decompiler/ContainerLoader.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.util.decompiler;\n\nimport org.jd.core.v1.api.loader.Loader;\nimport org.jd.core.v1.api.loader.LoaderException;\nimport org.jd.gui.api.model.Container;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\n\npublic class ContainerLoader implements Loader {\n    protected byte[] buffer = new byte[1024 * 4];\n    protected Container.Entry entry;\n\n    public ContainerLoader() { this.entry = null; }\n    public ContainerLoader(Container.Entry entry) {\n        this.entry = entry;\n    }\n\n    public void setEntry(Container.Entry e) { this.entry = e; }\n\n    protected Container.Entry getEntry(String internalPath) {\n        String path = internalPath + \".class\";\n\n        if (entry.getPath().equals(path)) {\n            return entry;\n        }\n        for (Container.Entry e : entry.getParent().getChildren()) {\n            if (e.getPath().equals(path)) {\n                return e;\n            }\n        }\n        return null;\n    }\n\n    // --- Loader --- //\n    @Override\n    public boolean canLoad(String internalName) {\n        return getEntry(internalName) != null;\n    }\n\n    @Override\n    public byte[] load(String internalName) throws LoaderException {\n        Container.Entry entry = getEntry(internalName);\n\n        if (entry == null) {\n            return null;\n        } else {\n            try (InputStream input=entry.getInputStream(); ByteArrayOutputStream output=new ByteArrayOutputStream()) {\n                int len = input.read(buffer);\n\n                while (len > 0) {\n                    output.write(buffer, 0, len);\n                    len = input.read(buffer);\n                }\n\n                return output.toByteArray();\n            } catch (IOException e) {\n                throw new LoaderException(e);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/util/decompiler/LineNumberStringBuilderPrinter.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.util.decompiler;\n\npublic class LineNumberStringBuilderPrinter extends StringBuilderPrinter {\n    protected boolean showLineNumbers = false;\n\n    protected int maxLineNumber = 0;\n    protected int digitCount = 0;\n\n    protected String lineNumberBeginPrefix;\n    protected String lineNumberEndPrefix;\n    protected String unknownLineNumberPrefix;\n\n    public void setShowLineNumbers(boolean showLineNumbers) { this.showLineNumbers = showLineNumbers; }\n\n    protected int printDigit(int dcv, int lineNumber, int divisor, int left) {\n        if (digitCount >= dcv) {\n            if (lineNumber < divisor) {\n                stringBuffer.append(' ');\n            } else {\n                int e = (lineNumber-left) / divisor;\n                stringBuffer.append((char)('0' + e));\n                left += e*divisor;\n            }\n        }\n\n        return left;\n    }\n\n    // --- Printer --- //\n    @Override\n    public void start(int maxLineNumber, int majorVersion, int minorVersion) {\n        super.start(maxLineNumber, majorVersion, minorVersion);\n\n        if (showLineNumbers) {\n            this.maxLineNumber = maxLineNumber;\n\n            if (maxLineNumber > 0) {\n                digitCount = 1;\n                unknownLineNumberPrefix = \" \";\n                int maximum = 9;\n\n                while (maximum < maxLineNumber) {\n                    digitCount++;\n                    unknownLineNumberPrefix += ' ';\n                    maximum = maximum*10 + 9;\n                }\n\n                lineNumberBeginPrefix = \"/* \";\n                lineNumberEndPrefix = \" */ \";\n            } else {\n                unknownLineNumberPrefix = \"\";\n                lineNumberBeginPrefix = \"\";\n                lineNumberEndPrefix = \"\";\n            }\n        } else {\n            this.maxLineNumber = 0;\n            unknownLineNumberPrefix = \"\";\n            lineNumberBeginPrefix = \"\";\n            lineNumberEndPrefix = \"\";\n        }\n    }\n\n    @Override public void startLine(int lineNumber) {\n        if (maxLineNumber > 0) {\n            stringBuffer.append(lineNumberBeginPrefix);\n\n            if (lineNumber == UNKNOWN_LINE_NUMBER) {\n                stringBuffer.append(unknownLineNumberPrefix);\n            } else {\n                int left = 0;\n\n                left = printDigit(5, lineNumber, 10000, left);\n                left = printDigit(4, lineNumber,  1000, left);\n                left = printDigit(3, lineNumber,   100, left);\n                left = printDigit(2, lineNumber,    10, left);\n                stringBuffer.append((char)('0' + (lineNumber-left)));\n            }\n\n            stringBuffer.append(lineNumberEndPrefix);\n        }\n\n        for (int i=0; i<indentationCount; i++) {\n            stringBuffer.append(TAB);\n        }\n    }\n    @Override public void extraLine(int count) {\n        if (realignmentLineNumber) {\n            while (count-- > 0) {\n                if (maxLineNumber > 0) {\n                    stringBuffer.append(lineNumberBeginPrefix);\n                    stringBuffer.append(unknownLineNumberPrefix);\n                    stringBuffer.append(lineNumberEndPrefix);\n                }\n\n                stringBuffer.append(NEWLINE);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/util/decompiler/NopPrinter.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.util.decompiler;\n\nimport org.jd.core.v1.api.printer.Printer;\n\npublic class NopPrinter implements Printer {\n\t@Override public void start(int maxLineNumber, int majorVersion, int minorVersion) {}\n\t@Override public void end() {}\n\n\t@Override public void printText(String text) {}\n\t@Override public void printNumericConstant(String constant) {}\n\t@Override public void printStringConstant(String constant, String ownerInternalName) {}\n\t@Override public void printKeyword(String keyword) {}\n\n\t@Override public void printDeclaration(int flags, String internalTypeName, String name, String descriptor) {}\n\t@Override public void printReference(int flags, String internalTypeName, String name, String descriptor, String ownerInternalName) {}\n\n\t@Override public void indent() {}\n\t@Override public void unindent() {}\n\n\t@Override public void startLine(int lineNumber) {}\n\t@Override public void endLine() {}\n\t@Override public void extraLine(int count) {}\n\n\t@Override public void startMarker(int type) {}\n\t@Override public void endMarker(int type) {}\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/util/decompiler/StringBuilderPrinter.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.util.decompiler;\n\nimport org.jd.core.v1.api.printer.Printer;\n\npublic class StringBuilderPrinter implements Printer {\n    protected static final String TAB = \"  \";\n    protected static final String NEWLINE = \"\\n\";\n\n    protected StringBuilder stringBuffer = new StringBuilder(10*1024);\n\n    protected boolean unicodeEscape = true;\n    protected boolean realignmentLineNumber = false;\n\n    protected int majorVersion = 0;\n    protected int minorVersion = 0;\n    protected int indentationCount;\n\n    public void setUnicodeEscape(boolean unicodeEscape) { this.unicodeEscape = unicodeEscape; }\n    public void setRealignmentLineNumber(boolean realignmentLineNumber) { this.realignmentLineNumber = realignmentLineNumber; }\n\n    public int getMajorVersion() { return majorVersion; }\n    public int getMinorVersion() { return minorVersion; }\n    public StringBuilder getStringBuffer() { return stringBuffer; }\n\n    protected void escape(String s) {\n        if (unicodeEscape && (s != null)) {\n            int length = s.length();\n\n            for (int i=0; i<length; i++) {\n                char c = s.charAt(i);\n\n                if (c == '\\t') {\n                    stringBuffer.append(c);\n                } else if (c < 32) {\n                    // Write octal format\n                    stringBuffer.append(\"\\\\0\");\n                    stringBuffer.append((char) ('0' + (c >> 3)));\n                    stringBuffer.append((char) ('0' + (c & 0x7)));\n                } else if (c > 127) {\n                    // Write octal format\n                    stringBuffer.append(\"\\\\u\");\n\n                    int z = (c >> 12);\n                    stringBuffer.append((char) ((z <= 9) ? ('0' + z) : (('A' - 10) + z)));\n                    z = ((c >> 8) & 0xF);\n                    stringBuffer.append((char) ((z <= 9) ? ('0' + z) : (('A' - 10) + z)));\n                    z = ((c >> 4) & 0xF);\n                    stringBuffer.append((char) ((z <= 9) ? ('0' + z) : (('A' - 10) + z)));\n                    z = (c & 0xF);\n                    stringBuffer.append((char) ((z <= 9) ? ('0' + z) : (('A' - 10) + z)));\n                } else {\n                    stringBuffer.append(c);\n                }\n            }\n        } else {\n            stringBuffer.append(s);\n        }\n    }\n\n    // --- Printer --- //\n    @Override\n    public void start(int maxLineNumber, int majorVersion, int minorVersion) {\n        this.stringBuffer.setLength(0);\n        this.majorVersion = majorVersion;\n        this.minorVersion = minorVersion;\n        this.indentationCount = 0;\n    }\n\n    @Override public void end() {}\n\n    @Override public void printText(String text) { escape(text); }\n    @Override public void printNumericConstant(String constant) { escape(constant); }\n    @Override public void printStringConstant(String constant, String ownerInternalName) { escape(constant); }\n    @Override public void printKeyword(String keyword) { stringBuffer.append(keyword); }\n\n    @Override public void printDeclaration(int type, String internalTypeName, String name, String descriptor) { escape(name); }\n    @Override public void printReference(int type, String internalTypeName, String name, String descriptor, String ownerInternalName) { escape(name); }\n\n    @Override public void indent() { indentationCount++; }\n    @Override public void unindent() { if (indentationCount > 0) indentationCount--; }\n\n    @Override public void startLine(int lineNumber) { for (int i=0; i<indentationCount; i++) stringBuffer.append(TAB); }\n    @Override public void endLine() { stringBuffer.append(NEWLINE); }\n    @Override public void extraLine(int count) { if (realignmentLineNumber) while (count-- > 0) stringBuffer.append(NEWLINE); }\n\n    @Override public void startMarker(int type) {}\n    @Override public void endMarker(int type) {}\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/util/exception/ExceptionUtil.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.util.exception;\n\npublic class ExceptionUtil {\n    public static boolean printStackTrace(Throwable throwable) {\n        throwable.printStackTrace();\n        return true;\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/util/index/IndexesUtil.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.util.index;\n\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.Future;\n\npublic class IndexesUtil {\n    public static boolean containsInternalTypeName(Collection<Future<Indexes>> collectionOfFutureIndexes, String internalTypeName) {\n        return contains(collectionOfFutureIndexes, \"typeDeclarations\", internalTypeName);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static List<Container.Entry> findInternalTypeName(Collection<Future<Indexes>> collectionOfFutureIndexes, String internalTypeName) {\n        return find(collectionOfFutureIndexes, \"typeDeclarations\", internalTypeName);\n    }\n\n    public static boolean contains(Collection<Future<Indexes>> collectionOfFutureIndexes, String indexName, String key) {\n        try {\n            for (Future<Indexes> futureIndexes : collectionOfFutureIndexes) {\n                if (futureIndexes.isDone()) {\n                    Map<String, Collection> index = futureIndexes.get().getIndex(indexName);\n                    if ((index != null) && (index.get(key) != null)) {\n                        return true;\n                    }\n                }\n            }\n        } catch (Exception e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n\n        return false;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static List<Container.Entry> find(Collection<Future<Indexes>> collectionOfFutureIndexes, String indexName, String key) {\n        ArrayList<Container.Entry> entries = new ArrayList<>();\n\n        try {\n            for (Future<Indexes> futureIndexes : collectionOfFutureIndexes) {\n                if (futureIndexes.isDone()) {\n                    Map<String, Collection> index = futureIndexes.get().getIndex(indexName);\n                    if (index != null) {\n                        Collection<Container.Entry> collection = index.get(key);\n                        if (collection != null) {\n                            entries.addAll(collection);\n                        }\n                    }\n                }\n            }\n        } catch (Exception e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n\n        return entries;\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/util/io/NewlineOutputStream.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.util.io;\n\nimport java.io.FilterOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.nio.charset.Charset;\n\npublic class NewlineOutputStream extends FilterOutputStream {\n    private static byte[] lineSeparator;\n\n    public NewlineOutputStream(OutputStream os) {\n        super(os);\n\n        if (lineSeparator == null) {\n            String s = System.getProperty(\"line.separator\");\n\n            if ((s == null) || (s.length() <= 0))\n                s = \"\\n\";\n\n            lineSeparator = s.getBytes(Charset.forName(\"UTF-8\"));\n        }\n    }\n\n    public void write(int b) throws IOException {\n        if (b == '\\n') {\n            out.write(lineSeparator);\n        } else {\n            out.write(b);\n        }\n    }\n\n    public void write(byte b[]) throws IOException {\n        write(b, 0, b.length);\n    }\n\n    public void write(byte b[], int off, int len) throws IOException {\n        int i;\n\n        for (i=off; i<len; i++) {\n            if (b[i] == '\\n') {\n                out.write(b, off, i-off);\n                out.write(lineSeparator);\n                off = i+1;\n            }\n        }\n\n        out.write(b, off, i-off);\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/util/io/TextReader.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.util.io;\n\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport java.io.*;\n\npublic class TextReader {\n\n    public static String getText(File file) {\n        try {\n            return getText(new FileInputStream(file));\n        } catch (IOException e) {\n            assert ExceptionUtil.printStackTrace(e);\n            return \"\";\n        }\n    }\n\n    public static String getText(InputStream is) {\n        StringBuilder sb = new StringBuilder();\n        char[] charBuffer = new char[8192];\n        int nbCharRead;\n\n        try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {\n            while ((nbCharRead = reader.read(charBuffer)) != -1) {\n                // appends buffer\n                sb.append(charBuffer, 0, nbCharRead);\n            }\n        } catch (IOException e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n\n        return sb.toString();\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/util/matcher/DescriptorMatcher.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.util.matcher;\n\n/*\n * Descriptor format : @see jd.gui.api.feature.UriOpenable\n */\npublic class DescriptorMatcher {\n\n    public static boolean matchFieldDescriptors(String d1, String d2) {\n        return matchDescriptors(new CharBuffer(d1), new CharBuffer(d2));\n    }\n\n    protected static boolean matchDescriptors(CharBuffer cb1, CharBuffer cb2) {\n        if (cb1.read() == '?') {\n            if (cb2.read() == '?') {\n                return true;\n            } else {\n                cb2.unread();\n                return cb2.skipType();\n            }\n        } else {\n            cb1.unread();\n\n            if (cb2.read() == '?') {\n                return cb1.skipType();\n            } else {\n                cb2.unread();\n                return cb1.compareTypeWith(cb2);\n            }\n        }\n    }\n\n    public static boolean matchMethodDescriptors(String d1, String d2) {\n        CharBuffer cb1 = new CharBuffer(d1);\n        CharBuffer cb2 = new CharBuffer(d2);\n\n        if ((cb1.read() != '(') || (cb2.read() != '('))\n            return false;\n\n        if (cb1.read() == '*') {\n            return true;\n        }\n        if (cb2.read() == '*') {\n            return true;\n        }\n\n        cb1.unread();\n        cb2.unread();\n\n        // Check parameter descriptors\n        while (cb2.get() != ')') {\n            if (!matchDescriptors(cb1, cb2))\n                return false;\n        }\n\n        if ((cb1.read() != ')') || (cb2.read() != ')'))\n            return false;\n\n        // Check return descriptor\n        return matchDescriptors(cb1, cb2);\n    }\n\n    protected static class CharBuffer {\n        protected char[] buffer;\n        protected int length;\n        protected int offset;\n\n        public CharBuffer(String s) {\n            this.buffer = s.toCharArray();\n            this.length = buffer.length;\n            this.offset = 0;\n        }\n\n        public char read() {\n            if (offset < length)\n                return buffer[offset++];\n            else\n                return (char)0;\n        }\n\n        public boolean unread() {\n            if (offset > 0) {\n                offset--;\n                return true;\n            } else {\n                return false;\n            }\n        }\n\n        public char get() {\n            if (offset < length)\n                return buffer[offset];\n            else\n                return (char)0;\n        }\n\n        public boolean skipType() {\n            if (offset < length) {\n                char c = buffer[offset++];\n\n                while ((c == '[') && (offset < length)) {\n                    c = buffer[offset++];\n                }\n\n                if (c == 'L') {\n                    while (offset < length) {\n                        if (buffer[offset++] == ';')\n                            return true;\n                    }\n                } else if (c != '[') {\n                    return true;\n                }\n            }\n            return false;\n        }\n\n        public boolean compareTypeWith(CharBuffer other) {\n            if (offset >= length)\n                return false;\n\n            char c = buffer[offset++];\n\n            if (c != other.read())\n                return false;\n\n            if (c == 'L') {\n                if ((offset >= length) || (other.offset >= other.length))\n                    return false;\n\n                char[] otherBuffer = other.buffer;\n\n                if ((buffer[offset] == '*') || (otherBuffer[other.offset] == '*')) {\n                    int start = offset;\n                    int otherStart = other.offset;\n\n                    // Search ';'\n                    if (!searchEndOfType() || !other.searchEndOfType())\n                        return false;\n\n                    int current = offset - 1;\n                    int otherCurrent = other.offset - 1;\n\n                    // Backward comparison\n                    while ((start < current) && (otherStart < otherCurrent)) {\n                        c = buffer[--current];\n                        if (c == '*')\n                            return true;\n\n                        char otherC = otherBuffer[--otherCurrent];\n                        if (otherC == '*')\n                            return true;\n                        if (c != otherC)\n                            return false;\n                    }\n                } else {\n                    // Forward comparison\n                    while (offset < length) {\n                        c = buffer[offset++];\n                        if (c != other.read())\n                            return false;\n                        if (c == ';')\n                            return true;\n                    }\n                    return false;\n                }\n            }\n\n            return true;\n        }\n\n        protected boolean searchEndOfType() {\n            while (offset < length) {\n                if (buffer[offset++] == ';')\n                    return true;\n            }\n            return false;\n        }\n\n        public String toString() {\n            return new String(buffer, offset, length-offset);\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/util/parser/antlr/ANTLRJavaParser.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.util.parser.antlr;\n\nimport org.antlr.v4.runtime.CharStream;\nimport org.antlr.v4.runtime.CommonTokenStream;\nimport org.antlr.v4.runtime.tree.ParseTree;\nimport org.antlr.v4.runtime.tree.ParseTreeWalker;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\npublic class ANTLRJavaParser {\n    public static void parse(CharStream input, JavaListener listener) {\n        try {\n            JavaLexer lexer = new JavaLexer(input);\n\n            lexer.removeErrorListeners();\n\n            CommonTokenStream tokens = new CommonTokenStream(lexer);\n            JavaParser parser = new JavaParser(tokens);\n\n            parser.removeErrorListeners();\n\n            ParseTree tree = parser.compilationUnit();\n\n            ParseTreeWalker.DEFAULT.walk(listener, tree);\n        } catch (StackOverflowError e) {\n            // Too complex source file, probably not written by a human.\n            // This error may happen on Java file generated by ANTLR for example.\n            assert ExceptionUtil.printStackTrace(e);\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/util/parser/antlr/AbstractJavaListener.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.util.parser.antlr;\n\nimport org.antlr.v4.runtime.tree.ParseTree;\nimport org.antlr.v4.runtime.tree.TerminalNode;\nimport org.antlr.v4.runtime.tree.TerminalNodeImpl;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport java.util.HashMap;\nimport java.util.List;\n\npublic abstract class AbstractJavaListener extends JavaBaseListener {\n    protected Container.Entry entry;\n    protected String packageName = \"\";\n    protected HashMap<String, String> nameToInternalTypeName = new HashMap<>();\n    protected StringBuilder sb = new StringBuilder();\n    protected HashMap<String, String> typeNameCache = new HashMap<>();\n\n    public AbstractJavaListener(Container.Entry entry) {\n        this.entry = entry;\n    }\n\n    public void enterPackageDeclaration(JavaParser.PackageDeclarationContext ctx) {\n        packageName = concatIdentifiers(ctx.qualifiedName().Identifier());\n    }\n\n    public void enterImportDeclaration(JavaParser.ImportDeclarationContext ctx) {\n        List<TerminalNode> identifiers = ctx.qualifiedName().Identifier();\n        int size = identifiers.size();\n\n        if (size > 1) {\n            nameToInternalTypeName.put(identifiers.get(size - 1).getText(), concatIdentifiers(identifiers));\n        }\n    }\n\n    protected String concatIdentifiers(List<TerminalNode> identifiers) {\n        switch (identifiers.size()) {\n            case 0:\n                return \"\";\n            case 1:\n                return identifiers.get(0).getText();\n            default:\n                sb.setLength(0);\n\n                for (TerminalNode identifier : identifiers) {\n                    sb.append(identifier.getText()).append('/');\n                }\n\n                // Remove last separator\n                sb.setLength(sb.length() - 1);\n\n                return sb.toString();\n        }\n    }\n\n    protected String resolveInternalTypeName(List<TerminalNode> identifiers) {\n        switch (identifiers.size()) {\n            case 0:\n                return null;\n\n            case 1:\n                // Search in cache\n                String name = identifiers.get(0).getText();\n                String qualifiedName = typeNameCache.get(name);\n\n                if (qualifiedName != null) {\n                    return qualifiedName;\n                }\n\n                // Search in imports\n                String imp = nameToInternalTypeName.get(name);\n\n                if (imp != null) {\n                    // Import found\n                    return imp;\n                }\n\n                // Search type in same package\n                String prefix = name + '.';\n\n                if (entry.getPath().indexOf('/') != -1) {\n                    // Not in root package\n                    Container.Entry parent = entry.getParent();\n                    int packageLength = parent.getPath().length() + 1;\n\n                    for (Container.Entry child : parent.getChildren()) {\n                        if (!child.isDirectory() && child.getPath().substring(packageLength).startsWith(prefix)) {\n                            qualifiedName = packageName + '/' + name;\n                            typeNameCache.put(name, qualifiedName);\n                            return qualifiedName;\n                        }\n                    }\n                }\n\n                // Search type in root package\n                for (Container.Entry child : entry.getContainer().getRoot().getChildren()) {\n                    if (!child.isDirectory() && child.getPath().startsWith(prefix)) {\n                        typeNameCache.put(name, name);\n                        return name;\n                    }\n                }\n\n                // Search type in 'java.lang'\n                try {\n                    if (Class.forName(\"java.lang.\" + name) != null) {\n                        qualifiedName = \"java/lang/\" + name;\n                        typeNameCache.put(name, qualifiedName);\n                        return qualifiedName;\n                    }\n                } catch (ClassNotFoundException ignore) {\n                    // Ignore class loading error\n                }\n\n                // Type not found\n                qualifiedName = \"*/\" + name;\n                typeNameCache.put(name, qualifiedName);\n                return qualifiedName;\n\n            default:\n                // Qualified type name -> Nothing to do\n                return concatIdentifiers(identifiers);\n        }\n    }\n\n    protected String createDescriptor(JavaParser.TypeContext typeContext, int dimension) {\n        if (typeContext == null) {\n            return \"V\";\n        } else {\n            dimension += countDimension(typeContext.children);\n            JavaParser.PrimitiveTypeContext primitive = typeContext.primitiveType();\n            String name;\n\n            if (primitive == null) {\n                JavaParser.ClassOrInterfaceTypeContext type = typeContext.classOrInterfaceType();\n                List<JavaParser.TypeArgumentsContext> typeArgumentsContexts = type.typeArguments();\n\n                if (typeArgumentsContexts.size() == 1) {\n                    JavaParser.TypeArgumentsContext typeArgumentsContext = typeArgumentsContexts.get(0);\n                    List<JavaParser.TypeArgumentContext> typeArguments = typeArgumentsContext.typeArgument();\n                } else if (typeArgumentsContexts.size() > 1) {\n                    throw new RuntimeException(\"UNEXPECTED\");\n                }\n\n                name = \"L\" + resolveInternalTypeName(type.Identifier()) + \";\";\n            } else {\n                // Search primitive\n                switch (primitive.getText()) {\n                    case \"boolean\": name = \"Z\"; break;\n                    case \"byte\":    name = \"B\"; break;\n                    case \"char\":    name = \"C\"; break;\n                    case \"double\":  name = \"D\"; break;\n                    case \"float\":   name = \"F\"; break;\n                    case \"int\":     name = \"I\"; break;\n                    case \"long\":    name = \"J\"; break;\n                    case \"short\":   name = \"S\"; break;\n                    case \"void\":    name = \"V\"; break;\n                    default:\n                        throw new RuntimeException(\"UNEXPECTED PRIMITIVE\");\n                }\n            }\n\n            switch (dimension) {\n                case 0:  return name;\n                case 1:  return \"[\" + name;\n                case 2:  return \"[[\" + name;\n                default: return new String(new char[dimension]).replace('\\0', '[') + name;\n            }\n        }\n    }\n\n    protected int countDimension(List<ParseTree> children) {\n        int dimension = 0;\n\n        for (ParseTree child : children) {\n            if (child instanceof TerminalNodeImpl) {\n                if (((TerminalNodeImpl)child).getSymbol().getType() == JavaParser.LBRACK)\n                    dimension++;\n            }\n        }\n\n        return dimension;\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/util/xml/AbstractXmlPathFinder.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.util.xml;\n\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport javax.xml.stream.XMLInputFactory;\nimport javax.xml.stream.XMLStreamException;\nimport javax.xml.stream.XMLStreamReader;\nimport java.io.StringReader;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.HashSet;\n\npublic abstract class AbstractXmlPathFinder {\n    protected HashMap<String, HashSet<String>> tagNameToPaths = new HashMap<>();\n    protected StringBuilder sb = new StringBuilder(200);\n\n    public AbstractXmlPathFinder(Collection<String> paths) {\n        for (String path : paths) {\n            if ((path != null) && (path.length() > 0)) {\n                // Normalize path\n                path = '/' + path;\n                int lastIndex = path.lastIndexOf('/');\n                String lastTagName = path.substring(lastIndex+1);\n\n                // Add tag names to map\n                HashSet<String> setOfPaths = tagNameToPaths.get(lastTagName);\n                if (setOfPaths == null) {\n                    tagNameToPaths.put(lastTagName, setOfPaths = new HashSet<>());\n                }\n                setOfPaths.add(path);\n            }\n        }\n    }\n\n    public void find(String text) {\n        sb.setLength(0);\n\n        try {\n            XMLInputFactory factory = XMLInputFactory.newInstance();\n            XMLStreamReader reader = factory.createXMLStreamReader(new StringReader(text));\n\n            String tagName = \"\";\n            int offset = 0;\n\n            while (reader.hasNext()) {\n                reader.next();\n\n                switch (reader.getEventType())\n                {\n                case XMLStreamReader.START_ELEMENT:\n                    sb.append('/').append(tagName = reader.getLocalName());\n                    offset = reader.getLocation().getCharacterOffset();\n                    break;\n                case XMLStreamReader.END_ELEMENT:\n                    sb.setLength(sb.length() - reader.getLocalName().length() - 1);\n                    break;\n                case XMLStreamReader.CHARACTERS:\n                    HashSet<String> setOfPaths = tagNameToPaths.get(tagName);\n\n                    if (setOfPaths != null) {\n                        String path = sb.toString();\n\n                        if (setOfPaths.contains(path)) {\n                            // Search start offset\n                            while (offset > 0) {\n                                if (text.charAt(offset) == '>') {\n                                    break;\n                                } else {\n                                    offset--;\n                                }\n                            }\n\n                            handle(path.substring(1), reader.getText(), offset+1);\n                        }\n                    }\n                    break;\n                }\n            }\n        } catch (XMLStreamException e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n    }\n\n    public abstract void handle(String path, String text, int position);\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/view/component/AbstractTextPage.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view.component;\n\nimport org.fife.ui.rsyntaxtextarea.*;\nimport org.fife.ui.rsyntaxtextarea.folding.FoldManager;\nimport org.fife.ui.rtextarea.*;\nimport org.jd.gui.api.feature.ContentSearchable;\nimport org.jd.gui.api.feature.LineNumberNavigable;\nimport org.jd.gui.api.feature.PreferencesChangeListener;\nimport org.jd.gui.api.feature.UriOpenable;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport javax.swing.*;\nimport javax.swing.text.BadLocationException;\nimport java.awt.*;\nimport java.awt.event.KeyEvent;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.awt.event.MouseWheelListener;\nimport java.io.IOException;\nimport java.io.UnsupportedEncodingException;\nimport java.net.URI;\nimport java.net.URLDecoder;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class AbstractTextPage extends JPanel implements LineNumberNavigable, ContentSearchable, UriOpenable, PreferencesChangeListener {\n    protected static final String FONT_SIZE_KEY = \"ViewerPreferences.fontSize\";\n\n    protected static final ImageIcon COLLAPSED_ICON = new ImageIcon(AbstractTextPage.class.getClassLoader().getResource(\"org/jd/gui/images/plus.png\"));\n    protected static final ImageIcon EXPANDED_ICON = new ImageIcon(AbstractTextPage.class.getClassLoader().getResource(\"org/jd/gui/images/minus.png\"));\n\n    protected static final Color DOUBLE_CLICK_HIGHLIGHT_COLOR = new Color(0x66ff66);\n    protected static final Color SEARCH_HIGHLIGHT_COLOR = new Color(0xffff66);\n    protected static final Color SELECT_HIGHLIGHT_COLOR = new Color(0xF49810);\n\n    protected static final RSyntaxTextAreaEditorKit.DecreaseFontSizeAction DECREASE_FONT_SIZE_ACTION = new RSyntaxTextAreaEditorKit.DecreaseFontSizeAction();\n    protected static final RSyntaxTextAreaEditorKit.IncreaseFontSizeAction INCREASE_FONT_SIZE_ACTION = new RSyntaxTextAreaEditorKit.IncreaseFontSizeAction();\n\n    protected RSyntaxTextArea textArea;\n    protected RTextScrollPane scrollPane;\n\n    protected Map<String, String> preferences;\n\n    public AbstractTextPage() {\n        super(new BorderLayout());\n\n        textArea = newSyntaxTextArea();\n        textArea.setSyntaxEditingStyle(getSyntaxStyle());\n        textArea.setCodeFoldingEnabled(true);\n        textArea.setAntiAliasingEnabled(true);\n        textArea.setCaretPosition(0);\n        textArea.setEditable(false);\n        textArea.setDropTarget(null);\n        textArea.setPopupMenu(null);\n        textArea.addMouseListener(new MouseAdapter() {\n            public void mouseClicked(MouseEvent e) {\n                if (e.getClickCount() == 2) {\n                    textArea.setMarkAllHighlightColor(DOUBLE_CLICK_HIGHLIGHT_COLOR);\n                    SearchEngine.markAll(textArea, newSearchContext(textArea.getSelectedText(), true, true, true, false));\n                }\n            }\n        });\n\n        KeyStroke ctrlA = KeyStroke.getKeyStroke(KeyEvent.VK_A, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());\n        KeyStroke ctrlC = KeyStroke.getKeyStroke(KeyEvent.VK_C, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());\n        KeyStroke ctrlV = KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());\n        InputMap inputMap = textArea.getInputMap();\n        inputMap.put(ctrlA, \"none\");\n        inputMap.put(ctrlC, \"none\");\n        inputMap.put(ctrlV, \"none\");\n\n        try {\n            Theme theme = Theme.load(getClass().getClassLoader().getResourceAsStream(\"rsyntaxtextarea/themes/eclipse.xml\"));\n            theme.apply(textArea);\n        } catch (IOException e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n\n        scrollPane = new RTextScrollPane(textArea);\n        scrollPane.setFoldIndicatorEnabled(true);\n        scrollPane.setFont(textArea.getFont());\n\n        final MouseWheelListener[] mouseWheelListeners = scrollPane.getMouseWheelListeners();\n\n        // Remove default listeners\n        for (MouseWheelListener listener : mouseWheelListeners) {\n            scrollPane.removeMouseWheelListener(listener);\n        }\n\n        scrollPane.addMouseWheelListener(e -> {\n            if ((e.getModifiers() & (Event.META_MASK|Event.CTRL_MASK)) != 0) {\n                int x = e.getX() + scrollPane.getX() - textArea.getX();\n                int y = e.getY() + scrollPane.getY() - textArea.getY();\n                int offset = textArea.viewToModel(new Point(x, y));\n\n                // Update font size\n                if (e.getWheelRotation() > 0) {\n                    DECREASE_FONT_SIZE_ACTION.actionPerformedImpl(null, textArea);\n                } else {\n                    INCREASE_FONT_SIZE_ACTION.actionPerformedImpl(null, textArea);\n                }\n\n                // Save preferences\n                if (preferences != null) {\n                    preferences.put(FONT_SIZE_KEY, String.valueOf(textArea.getFont().getSize()));\n                }\n\n                try {\n                    Rectangle newRectangle = textArea.modelToView(offset);\n                    int newY = newRectangle.y + (newRectangle.height >> 1);\n\n                    // Scroll\n                    Point viewPosition = scrollPane.getViewport().getViewPosition();\n                    viewPosition.y = Math.max(viewPosition.y +newY - y, 0);\n                    scrollPane.getViewport().setViewPosition(viewPosition);\n                } catch (BadLocationException ee) {\n                    assert ExceptionUtil.printStackTrace(ee);\n                }\n            } else {\n                // Call default listeners\n                for (MouseWheelListener listener : mouseWheelListeners) {\n                    listener.mouseWheelMoved(e);\n                }\n            }\n        });\n\n        Gutter gutter = scrollPane.getGutter();\n        gutter.setFoldIcons(COLLAPSED_ICON, EXPANDED_ICON);\n        gutter.setFoldIndicatorForeground(gutter.getBorderColor());\n\n        add(scrollPane, BorderLayout.CENTER);\n        add(new RoundMarkErrorStrip(textArea), BorderLayout.LINE_END);\n    }\n\n    protected RSyntaxTextArea newSyntaxTextArea() { return new RSyntaxTextArea(); }\n\n    public String getText() { return textArea.getText(); }\n\n    public JScrollPane getScrollPane() {\n        return scrollPane;\n    }\n\n    public void setText(String text) {\n        textArea.setText(text);\n        textArea.setCaretPosition(0);\n    }\n\n    public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_NONE; }\n\n    /**\n     * @see org.fife.ui.rsyntaxtextarea.RSyntaxUtilities#selectAndPossiblyCenter\n     * Force center and do not select\n     */\n    public void setCaretPositionAndCenter(DocumentRange range) {\n        final int start = range.getStartOffset();\n        final int end = range.getEndOffset();\n        boolean foldsExpanded = false;\n        FoldManager fm = textArea.getFoldManager();\n\n        if (fm.isCodeFoldingSupportedAndEnabled()) {\n            foldsExpanded = fm.ensureOffsetNotInClosedFold(start);\n            foldsExpanded |= fm.ensureOffsetNotInClosedFold(end);\n        }\n\n        if (!foldsExpanded) {\n            try {\n                Rectangle rec = textArea.modelToView(start);\n\n                if (rec != null) {\n                    // Visible\n                    setCaretPositionAndCenter(start, end, rec);\n                } else {\n                    // Not visible yet\n                    SwingUtilities.invokeLater(() -> {\n                        try {\n                            Rectangle r = textArea.modelToView(start);\n                            if (r != null) {\n                                setCaretPositionAndCenter(start, end, r);\n                            }\n                        } catch (BadLocationException e) {\n                            assert ExceptionUtil.printStackTrace(e);\n                        }\n                    });\n                }\n            } catch (BadLocationException e) {\n                assert ExceptionUtil.printStackTrace(e);\n            }\n        }\n    }\n\n    protected void setCaretPositionAndCenter(int start, int end, Rectangle r) {\n        if (end != start) {\n            try {\n                r = r.union(textArea.modelToView(end));\n            } catch (BadLocationException e) {\n                assert ExceptionUtil.printStackTrace(e);\n            }\n        }\n\n        Rectangle visible = textArea.getVisibleRect();\n\n        // visible.x = r.x - (visible.width - r.width) / 2;\n        visible.y = r.y - (visible.height - r.height) / 2;\n\n        Rectangle bounds = textArea.getBounds();\n        Insets i = textArea.getInsets();\n        //bounds.x = i.left;\n        bounds.y = i.top;\n        //bounds.width -= i.left + i.right;\n        bounds.height -= i.top + i.bottom;\n\n        //if (visible.x < bounds.x) {\n        //    visible.x = bounds.x;\n        //}\n        //if (visible.x + visible.width > bounds.x + bounds.width) {\n        //    visible.x = bounds.x + bounds.width - visible.width;\n        //}\n        if (visible.y < bounds.y) {\n            visible.y = bounds.y;\n        }\n        if (visible.y + visible.height > bounds.y + bounds.height) {\n            visible.y = bounds.y + bounds.height - visible.height;\n        }\n\n        textArea.scrollRectToVisible(visible);\n        textArea.setCaretPosition(start);\n    }\n\n    // --- LineNumberNavigable --- //\n    public int getMaximumLineNumber() {\n        try {\n            return textArea.getLineOfOffset(textArea.getDocument().getLength()) + 1;\n        } catch (BadLocationException e) {\n            assert ExceptionUtil.printStackTrace(e);\n            return 0;\n        }\n    }\n\n    public void goToLineNumber(int lineNumber) {\n        try {\n            textArea.setCaretPosition(textArea.getLineStartOffset(lineNumber-1));\n        } catch (BadLocationException e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n    }\n\n    public boolean checkLineNumber(int lineNumber) { return true; }\n\n    // --- ContentSearchable --- //\n    public boolean highlightText(String text, boolean caseSensitive) {\n        if (text.length() > 1) {\n            textArea.setMarkAllHighlightColor(SEARCH_HIGHLIGHT_COLOR);\n            textArea.setCaretPosition(textArea.getSelectionStart());\n\n            SearchContext context = newSearchContext(text, caseSensitive, false, true, false);\n            SearchResult result = SearchEngine.find(textArea, context);\n\n            if (!result.wasFound()) {\n                textArea.setCaretPosition(0);\n                result = SearchEngine.find(textArea, context);\n            }\n\n            return result.wasFound();\n        } else {\n            return true;\n        }\n    }\n\n    public void findNext(String text, boolean caseSensitive) {\n        if (text.length() > 1) {\n            textArea.setMarkAllHighlightColor(SEARCH_HIGHLIGHT_COLOR);\n\n            SearchContext context = newSearchContext(text, caseSensitive, false, true, false);\n            SearchResult result = SearchEngine.find(textArea, context);\n\n            if (!result.wasFound()) {\n                textArea.setCaretPosition(0);\n                SearchEngine.find(textArea, context);\n            }\n        }\n    }\n\n    public void findPrevious(String text, boolean caseSensitive) {\n        if (text.length() > 1) {\n            textArea.setMarkAllHighlightColor(SEARCH_HIGHLIGHT_COLOR);\n\n            SearchContext context = newSearchContext(text, caseSensitive, false, false, false);\n            SearchResult result = SearchEngine.find(textArea, context);\n\n            if (!result.wasFound()) {\n                textArea.setCaretPosition(textArea.getDocument().getLength());\n                SearchEngine.find(textArea, context);\n            }\n        }\n    }\n\n    protected SearchContext newSearchContext(String searchFor, boolean matchCase, boolean wholeWord, boolean searchForward, boolean regexp) {\n        SearchContext context = new SearchContext(searchFor, matchCase);\n        context.setMarkAll(true);\n        context.setWholeWord(wholeWord);\n        context.setSearchForward(searchForward);\n        context.setRegularExpression(regexp);\n        return context;\n    }\n\n    // --- UriOpenable --- //\n    public boolean openUri(URI uri) {\n        String query = uri.getQuery();\n\n        if (query != null) {\n            Map<String, String> parameters = parseQuery(query);\n\n            if (parameters.containsKey(\"lineNumber\")) {\n                String lineNumber = parameters.get(\"lineNumber\");\n\n                try {\n                    goToLineNumber(Integer.parseInt(lineNumber));\n                    return true;\n                } catch (NumberFormatException e) {\n                    assert ExceptionUtil.printStackTrace(e);\n                }\n            } else if (parameters.containsKey(\"position\")) {\n                String position = parameters.get(\"position\");\n\n                try {\n                    int pos = Integer.parseInt(position);\n                    if (textArea.getDocument().getLength() > pos) {\n                        setCaretPositionAndCenter(new DocumentRange(pos, pos));\n                        return true;\n                    }\n                } catch (NumberFormatException e) {\n                    assert ExceptionUtil.printStackTrace(e);\n                }\n            } else if (parameters.containsKey(\"highlightFlags\")) {\n                String highlightFlags = parameters.get(\"highlightFlags\");\n\n                if ((highlightFlags.indexOf('s') != -1) && parameters.containsKey(\"highlightPattern\")) {\n                    textArea.setMarkAllHighlightColor(SELECT_HIGHLIGHT_COLOR);\n                    textArea.setCaretPosition(0);\n\n                    // Highlight all\n                    String searchFor = createRegExp(parameters.get(\"highlightPattern\"));\n                    SearchContext context =  newSearchContext(searchFor, true, false, true, true);\n                    SearchResult result = SearchEngine.find(textArea, context);\n\n                    if (result.getMatchRange() != null) {\n                        textArea.setCaretPosition(result.getMatchRange().getStartOffset());\n                    }\n\n                    return true;\n                }\n            }\n        }\n\n        return false;\n    }\n\n    protected Map<String, String> parseQuery(String query) {\n        HashMap<String, String> parameters = new HashMap<>();\n\n        // Parse parameters\n        try {\n            for (String param : query.split(\"&\")) {\n                int index = param.indexOf('=');\n\n                if (index == -1) {\n                    parameters.put(URLDecoder.decode(param, \"UTF-8\"), \"\");\n                } else {\n                    String key = param.substring(0, index);\n                    String value = param.substring(index + 1);\n                    parameters.put(URLDecoder.decode(key, \"UTF-8\"), URLDecoder.decode(value, \"UTF-8\"));\n                }\n            }\n        } catch (UnsupportedEncodingException e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n\n        return parameters;\n    }\n\n    /**\n     * Create a simple regular expression\n     *\n     * Rules:\n     *  '*'        matchTypeEntries 0 ou N characters\n     *  '?'        matchTypeEntries 1 character\n     */\n    public static String createRegExp(String pattern) {\n        int patternLength = pattern.length();\n        StringBuilder sbPattern = new StringBuilder(patternLength * 2);\n\n        for (int i = 0; i < patternLength; i++) {\n            char c = pattern.charAt(i);\n\n            if (c == '*') {\n                sbPattern.append(\".*\");\n            } else if (c == '?') {\n                sbPattern.append('.');\n            } else if (c == '.') {\n                sbPattern.append(\"\\\\.\");\n            } else {\n                sbPattern.append(c);\n            }\n        }\n\n        return sbPattern.toString();\n    }\n\n    // --- PreferencesChangeListener --- //\n    public void preferencesChanged(Map<String, String> preferences) {\n        String fontSize = preferences.get(FONT_SIZE_KEY);\n\n        if (fontSize != null) {\n            try {\n                textArea.setFont(textArea.getFont().deriveFont(Float.parseFloat(fontSize)));\n            } catch (Exception e) {\n                assert ExceptionUtil.printStackTrace(e);\n            }\n        }\n\n        this.preferences = preferences;\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/view/component/ClassFilePage.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view.component;\n\nimport org.fife.ui.rsyntaxtextarea.DocumentRange;\nimport org.fife.ui.rsyntaxtextarea.SyntaxConstants;\nimport org.jd.core.v1.ClassFileToJavaSourceDecompiler;\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.util.decompiler.*;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.util.io.NewlineOutputStream;\n\nimport javax.swing.text.BadLocationException;\nimport javax.swing.text.DefaultCaret;\nimport java.awt.*;\nimport java.io.*;\nimport java.nio.charset.Charset;\nimport java.util.HashMap;\nimport java.util.Map;\n\npublic class ClassFilePage extends TypePage {\n    protected static final String ESCAPE_UNICODE_CHARACTERS   = \"ClassFileDecompilerPreferences.escapeUnicodeCharacters\";\n    protected static final String REALIGN_LINE_NUMBERS        = \"ClassFileDecompilerPreferences.realignLineNumbers\";\n    protected static final String WRITE_LINE_NUMBERS          = \"ClassFileSaverPreferences.writeLineNumbers\";\n    protected static final String WRITE_METADATA              = \"ClassFileSaverPreferences.writeMetadata\";\n    protected static final String JD_CORE_VERSION             = \"JdGuiPreferences.jdCoreVersion\";\n\n    protected static final ClassFileToJavaSourceDecompiler DECOMPILER = new ClassFileToJavaSourceDecompiler();\n\n    protected int maximumLineNumber = -1;\n\n    static {\n        // Early class loading\n        try {\n            String internalTypeName = ClassFilePage.class.getName().replace('.', '/');\n            DECOMPILER.decompile(new ClassPathLoader(), new NopPrinter(), internalTypeName);\n        } catch (Throwable t) {\n            assert ExceptionUtil.printStackTrace(t);\n        }\n    }\n\n    public ClassFilePage(API api, Container.Entry entry) {\n        super(api, entry);\n        Map<String, String> preferences = api.getPreferences();\n        // Init view\n        setErrorForeground(Color.decode(preferences.get(\"JdGuiPreferences.errorBackgroundColor\")));\n        // Display source\n        decompile(preferences);\n    }\n\n    public void decompile(Map<String, String> preferences) {\n        try {\n            // Clear ...\n            clearHyperlinks();\n            clearLineNumbers();\n            declarations.clear();\n            typeDeclarations.clear();\n            strings.clear();\n\n            // Init preferences\n            boolean realignmentLineNumbers = getPreferenceValue(preferences, REALIGN_LINE_NUMBERS, false);\n            boolean unicodeEscape = getPreferenceValue(preferences, ESCAPE_UNICODE_CHARACTERS, false);\n\n            Map<String, Object> configuration = new HashMap<>();\n            configuration.put(\"realignLineNumbers\", realignmentLineNumbers);\n\n            setShowMisalignment(realignmentLineNumbers);\n\n            // Init loader\n            ContainerLoader loader = new ContainerLoader(entry);\n\n            // Init printer\n            ClassFilePrinter printer = new ClassFilePrinter();\n            printer.setRealignmentLineNumber(realignmentLineNumbers);\n            printer.setUnicodeEscape(unicodeEscape);\n\n            // Format internal name\n            String entryPath = entry.getPath();\n            assert entryPath.endsWith(\".class\");\n            String entryInternalName = entryPath.substring(0, entryPath.length() - 6); // 6 = \".class\".length()\n\n            // Decompile class file\n            DECOMPILER.decompile(loader, printer, entryInternalName, configuration);\n        } catch (Throwable t) {\n            assert ExceptionUtil.printStackTrace(t);\n            setText(\"// INTERNAL ERROR //\");\n        }\n\n        maximumLineNumber = getMaximumSourceLineNumber();\n    }\n\n    protected static boolean getPreferenceValue(Map<String, String> preferences, String key, boolean defaultValue) {\n        String v = preferences.get(key);\n        return (v == null) ? defaultValue : Boolean.valueOf(v);\n    }\n\n    @Override\n    public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_JAVA; }\n\n    // --- ContentSavable --- //\n    @Override\n    public String getFileName() {\n        String path = entry.getPath();\n        int index = path.lastIndexOf('.');\n        return path.substring(0, index) + \".java\";\n    }\n\n    @Override\n    public void save(API api, OutputStream os) {\n        try {\n            // Init preferences\n            Map<String, String> preferences = api.getPreferences();\n            boolean realignmentLineNumbers = getPreferenceValue(preferences, REALIGN_LINE_NUMBERS, false);\n            boolean unicodeEscape = getPreferenceValue(preferences, ESCAPE_UNICODE_CHARACTERS, false);\n            boolean showLineNumbers = getPreferenceValue(preferences, WRITE_LINE_NUMBERS, true);\n\n            Map<String, Object> configuration = new HashMap<>();\n            configuration.put(\"realignLineNumbers\", realignmentLineNumbers);\n\n            // Init loader\n            ContainerLoader loader = new ContainerLoader(entry);\n\n            // Init printer\n            LineNumberStringBuilderPrinter printer = new LineNumberStringBuilderPrinter();\n            printer.setRealignmentLineNumber(realignmentLineNumbers);\n            printer.setUnicodeEscape(unicodeEscape);\n            printer.setShowLineNumbers(showLineNumbers);\n\n            // Format internal name\n            String entryPath = entry.getPath();\n            assert entryPath.endsWith(\".class\");\n            String entryInternalName = entryPath.substring(0, entryPath.length() - 6); // 6 = \".class\".length()\n\n            // Decompile class file\n            DECOMPILER.decompile(loader, printer, entryInternalName, configuration);\n\n            StringBuilder stringBuffer = printer.getStringBuffer();\n\n            // Metadata\n            if (getPreferenceValue(preferences, WRITE_METADATA, true)) {\n                // Add location\n                String location =\n                        new File(entry.getUri()).getPath()\n                                // Escape \"\\ u\" sequence to prevent \"Invalid unicode\" errors\n                                .replaceAll(\"(^|[^\\\\\\\\])\\\\\\\\u\", \"\\\\\\\\\\\\\\\\u\");\n                stringBuffer.append(\"\\n\\n/* Location:              \");\n                stringBuffer.append(location);\n                // Add Java compiler version\n                int majorVersion = printer.getMajorVersion();\n\n                if (majorVersion >= 45) {\n                    stringBuffer.append(\"\\n * Java compiler version: \");\n\n                    if (majorVersion >= 49) {\n                        stringBuffer.append(majorVersion - (49 - 5));\n                    } else {\n                        stringBuffer.append(majorVersion - (45 - 1));\n                    }\n\n                    stringBuffer.append(\" (\");\n                    stringBuffer.append(majorVersion);\n                    stringBuffer.append('.');\n                    stringBuffer.append(printer.getMinorVersion());\n                    stringBuffer.append(')');\n                }\n                // Add JD-Core version\n                stringBuffer.append(\"\\n * JD-Core Version:       \");\n                stringBuffer.append(preferences.get(JD_CORE_VERSION));\n                stringBuffer.append(\"\\n */\");\n            }\n\n            try (PrintStream ps = new PrintStream(new NewlineOutputStream(os), true, \"UTF-8\")) {\n                ps.print(stringBuffer.toString());\n            } catch (IOException e) {\n                assert ExceptionUtil.printStackTrace(e);\n            }\n        } catch (Throwable t) {\n            assert ExceptionUtil.printStackTrace(t);\n\n            try (OutputStreamWriter writer = new OutputStreamWriter(os, Charset.defaultCharset())) {\n                writer.write(\"// INTERNAL ERROR //\");\n            } catch (IOException ee) {\n                assert ExceptionUtil.printStackTrace(ee);\n            }\n        }\n    }\n\n    // --- LineNumberNavigable --- //\n    @Override\n    public int getMaximumLineNumber() { return maximumLineNumber; }\n\n    @Override\n    public void goToLineNumber(int lineNumber) {\n        int textAreaLineNumber = getTextAreaLineNumber(lineNumber);\n        if (textAreaLineNumber > 0) {\n            try {\n                int start = textArea.getLineStartOffset(textAreaLineNumber - 1);\n                int end = textArea.getLineEndOffset(textAreaLineNumber - 1);\n                setCaretPositionAndCenter(new DocumentRange(start, end));\n            } catch (BadLocationException e) {\n                assert ExceptionUtil.printStackTrace(e);\n            }\n        }\n    }\n\n    @Override\n    public boolean checkLineNumber(int lineNumber) { return lineNumber <= maximumLineNumber; }\n\n    // --- PreferencesChangeListener --- //\n    @Override\n    public void preferencesChanged(Map<String, String> preferences) {\n        DefaultCaret caret = (DefaultCaret)textArea.getCaret();\n        int updatePolicy = caret.getUpdatePolicy();\n\n        caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);\n        decompile(preferences);\n        caret.setUpdatePolicy(updatePolicy);\n\n        super.preferencesChanged(preferences);\n    }\n\n    public class ClassFilePrinter extends StringBuilderPrinter {\n        protected HashMap<String, ReferenceData> referencesCache = new HashMap<>();\n\n        // Manage line number and misalignment\n        int textAreaLineNumber = 1;\n\n        @Override\n        public void start(int maxLineNumber, int majorVersion, int minorVersion) {\n            super.start(maxLineNumber, majorVersion, minorVersion);\n\n            if (maxLineNumber == 0) {\n                scrollPane.setLineNumbersEnabled(false);\n            } else {\n                setMaxLineNumber(maxLineNumber);\n            }\n        }\n\n        @Override\n        public void end() {\n            setText(stringBuffer.toString());\n        }\n\n        // --- Add strings --- //\n        @Override\n        public void printStringConstant(String constant, String ownerInternalName) {\n            if (constant == null) constant = \"null\";\n            if (ownerInternalName == null) ownerInternalName = \"null\";\n\n            strings.add(new TypePage.StringData(stringBuffer.length(), constant.length(), constant, ownerInternalName));\n            super.printStringConstant(constant, ownerInternalName);\n        }\n\n        @Override\n        public void printDeclaration(int type, String internalTypeName, String name, String descriptor) {\n            if (internalTypeName == null) internalTypeName = \"null\";\n            if (name == null) name = \"null\";\n            if (descriptor == null) descriptor = \"null\";\n\n            switch (type) {\n                case TYPE:\n                    TypePage.DeclarationData data = new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalTypeName, null, null);\n                    declarations.put(internalTypeName, data);\n                    typeDeclarations.put(stringBuffer.length(), data);\n                    break;\n                case CONSTRUCTOR:\n                    declarations.put(internalTypeName + \"-<init>-\" + descriptor, new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalTypeName, \"<init>\", descriptor));\n                    break;\n                default:\n                    declarations.put(internalTypeName + '-' + name + '-' + descriptor, new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalTypeName, name, descriptor));\n                    break;\n            }\n            super.printDeclaration(type, internalTypeName, name, descriptor);\n        }\n\n        @Override\n        public void printReference(int type, String internalTypeName, String name, String descriptor, String ownerInternalName) {\n            if (internalTypeName == null) internalTypeName = \"null\";\n            if (name == null) name = \"null\";\n            if (descriptor == null) descriptor = \"null\";\n\n            switch (type) {\n                case TYPE:\n                    addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalTypeName, null, null, ownerInternalName)));\n                    break;\n                case CONSTRUCTOR:\n                    addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalTypeName, \"<init>\", descriptor, ownerInternalName)));\n                    break;\n                default:\n                    addHyperlink(new TypePage.HyperlinkReferenceData(stringBuffer.length(), name.length(), newReferenceData(internalTypeName, name, descriptor, ownerInternalName)));\n                    break;\n            }\n            super.printReference(type, internalTypeName, name, descriptor, ownerInternalName);\n        }\n\n        @Override\n        public void startLine(int lineNumber) {\n            super.startLine(lineNumber);\n            setLineNumber(textAreaLineNumber, lineNumber);\n        }\n        @Override\n        public void endLine() {\n            super.endLine();\n            textAreaLineNumber++;\n        }\n        @Override\n        public void extraLine(int count) {\n            super.extraLine(count);\n            if (realignmentLineNumber) {\n                textAreaLineNumber += count;\n            }\n        }\n\n        // --- Add references --- //\n        public TypePage.ReferenceData newReferenceData(String internalName, String name, String descriptor, String scopeInternalName) {\n            String key = internalName + '-' + name + '-'+ descriptor + '-' + scopeInternalName;\n            ReferenceData reference = referencesCache.get(key);\n\n            if (reference == null) {\n                reference = new TypePage.ReferenceData(internalName, name, descriptor, scopeInternalName);\n                referencesCache.put(key, reference);\n                references.add(reference);\n            }\n\n            return reference;\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/view/component/CustomLineNumbersPage.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view.component;\n\nimport org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;\nimport org.fife.ui.rsyntaxtextarea.RSyntaxTextAreaEditorKit;\nimport org.fife.ui.rsyntaxtextarea.RSyntaxTextAreaUI;\nimport org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;\nimport org.fife.ui.rsyntaxtextarea.folding.Fold;\nimport org.fife.ui.rsyntaxtextarea.folding.FoldManager;\nimport org.fife.ui.rtextarea.Gutter;\nimport org.fife.ui.rtextarea.LineNumberList;\nimport org.fife.ui.rtextarea.RTextArea;\nimport org.fife.ui.rtextarea.RTextAreaUI;\n\nimport javax.swing.*;\nimport javax.swing.text.EditorKit;\nimport javax.swing.text.Element;\nimport javax.swing.text.JTextComponent;\nimport javax.swing.text.View;\nimport java.awt.*;\nimport java.util.Arrays;\nimport java.util.Map;\n\npublic abstract class CustomLineNumbersPage extends HyperlinkPage {\n    protected Color errorForeground = Color.RED;\n    protected boolean showMisalignment = true;\n\n    public void setErrorForeground(Color color) {\n        errorForeground = color;\n    }\n\n    public void setShowMisalignment(boolean b) {\n        showMisalignment = b;\n    }\n\n    /**\n     * Map[textarea line number] = original line number\n     */\n    protected int[] lineNumberMap = null;\n    protected int maxLineNumber = 0;\n\n    protected void setMaxLineNumber(int maxLineNumber) {\n        if (maxLineNumber > 0) {\n            if (lineNumberMap == null) {\n                lineNumberMap = new int[maxLineNumber+1];\n            } else if (lineNumberMap.length <= maxLineNumber) {\n                int[] tmp = new int[maxLineNumber+1];\n                System.arraycopy(lineNumberMap, 0, tmp, 0, lineNumberMap.length);\n                lineNumberMap = tmp;\n            }\n\n            this.maxLineNumber = maxLineNumber;\n        }\n    }\n\n    protected void initLineNumbers() {\n        String text = getText();\n        int len = text.length();\n\n        if (len == 0) {\n            setMaxLineNumber(0);\n        } else {\n            int mln = len - text.replace(\"\\n\", \"\").length();\n\n            if (text.charAt(len-1) != '\\n') {\n                mln++;\n            }\n\n            setMaxLineNumber(mln);\n\n            for (int i=1; i<=maxLineNumber; i++) {\n                lineNumberMap[i] = i;\n            }\n        }\n    }\n\n    protected void setLineNumber(int textAreaLineNumber, int originalLineNumber) {\n        if (originalLineNumber > 0) {\n            setMaxLineNumber(textAreaLineNumber);\n            lineNumberMap[textAreaLineNumber] = originalLineNumber;\n        }\n    }\n\n    protected void clearLineNumbers() {\n        if (lineNumberMap != null) {\n            Arrays.fill(lineNumberMap, 0);\n        }\n    }\n\n    protected int getMaximumSourceLineNumber() { return maxLineNumber; }\n\n    protected int getTextAreaLineNumber(int originalLineNumber) {\n        int textAreaLineNumber = 1;\n        int greatestLowerSourceLineNumber = 0;\n        int i = lineNumberMap.length;\n\n        while (i-- > 0) {\n            int sln = lineNumberMap[i];\n            if (sln <= originalLineNumber) {\n                if (greatestLowerSourceLineNumber < sln) {\n                    greatestLowerSourceLineNumber = sln;\n                    textAreaLineNumber = i;\n                }\n            }\n        }\n\n        return textAreaLineNumber;\n    }\n\n    @Override protected RSyntaxTextArea newSyntaxTextArea() { return new SourceSyntaxTextArea(); }\n\n    public class SourceSyntaxTextArea extends HyperlinkSyntaxTextArea {\n        @Override protected RTextAreaUI createRTextAreaUI() { return new SourceSyntaxTextAreaUI(this); }\n    }\n\n    /**\n     * A lot of code to replace the default LineNumberList...\n     */\n    public class SourceSyntaxTextAreaUI extends RSyntaxTextAreaUI {\n        public SourceSyntaxTextAreaUI(JComponent rSyntaxTextArea) { super(rSyntaxTextArea); }\n        @Override public EditorKit getEditorKit(JTextComponent tc) { return new SourceSyntaxTextAreaEditorKit(); }\n        @Override public Rectangle getVisibleEditorRect() { return super.getVisibleEditorRect(); }\n    }\n\n    public class SourceSyntaxTextAreaEditorKit extends RSyntaxTextAreaEditorKit {\n        @Override public LineNumberList createLineNumberList(RTextArea textArea) { return new SourceLineNumberList(textArea); }\n    }\n\n    /**\n     * Why 'LineNumberList' is so unexpandable ? Too many private fields & methods and too many package scope.\n     */\n    public class SourceLineNumberList extends LineNumberList {\n        protected RTextArea rTextArea;\n        protected Map<?,?> aaHints;\n        protected Rectangle visibleRect;\n        protected Insets textAreaInsets;\n        protected Dimension preferredSize;\n\n        public SourceLineNumberList(RTextArea textArea) {\n            super(textArea, null);\n            this.rTextArea = textArea;\n        }\n\n        @Override\n        protected void init() {\n            super.init();\n            visibleRect = new Rectangle();\n            aaHints = RSyntaxUtilities.getDesktopAntiAliasHints();\n            textAreaInsets = null;\n        }\n\n        /**\n         * @see org.fife.ui.rtextarea.LineNumberList#paintComponent(java.awt.Graphics)\n         */\n        @Override\n        protected void paintComponent(Graphics g) {\n            visibleRect = g.getClipBounds(visibleRect);\n\n            if (visibleRect == null) {\n                visibleRect = getVisibleRect();\n            }\n            if (visibleRect == null) {\n                return;\n            }\n\n            int cellWidth = getPreferredSize().width;\n            int cellHeight = rTextArea.getLineHeight();\n            int ascent = rTextArea.getMaxAscent();\n            FoldManager fm = ((RSyntaxTextArea)rTextArea).getFoldManager();\n            int RHS_BORDER_WIDTH = getRhsBorderWidth();\n            FontMetrics metrics = g.getFontMetrics();\n            int rhs = getWidth() - RHS_BORDER_WIDTH;\n\n            if (getParent() instanceof Gutter) { // Should always be true\n                g.setColor(getParent().getBackground());\n            } else {\n                g.setColor(getBackground());\n            }\n\n            g.fillRect(0, visibleRect.y, cellWidth, visibleRect.height);\n            g.setFont(getFont());\n\n            if (aaHints != null) {\n                ((Graphics2D)g).addRenderingHints(aaHints);\n            }\n\n            if (rTextArea.getLineWrap()) {\n                SourceSyntaxTextAreaUI ui = (SourceSyntaxTextAreaUI)rTextArea.getUI();\n                View v = ui.getRootView(rTextArea).getView(0);\n                Element root = rTextArea.getDocument().getDefaultRootElement();\n                int lineCount = root.getElementCount();\n                int topPosition = rTextArea.viewToModel(visibleRect.getLocation());\n                int topLine = root.getElementIndex(topPosition);\n                Rectangle visibleEditorRect = ui.getVisibleEditorRect();\n                Rectangle r = LineNumberList.getChildViewBounds(v, topLine, visibleEditorRect);\n                int y = r.y;\n\n                int visibleBottom =  visibleRect.y + visibleRect.height;\n\n                // Keep painting lines until our y-coordinate is past the visible\n                // end of the text area.\n\n                while (y < visibleBottom) {\n                    r = getChildViewBounds(v, topLine, visibleEditorRect);\n\n                    // Paint the line number.\n                    paintLineNumber(g, metrics, rhs, y+ascent, topLine + 1);\n\n                    // The next possible y-coordinate is just after the last line\n                    // painted.\n                    y += r.height;\n\n                    // Update topLine (we're actually using it for our \"current line\"\n                    // variable now).\n                    if (fm != null) {\n                        Fold fold = fm.getFoldForLine(topLine);\n                        if ((fold != null) && fold.isCollapsed()) {\n                            topLine += fold.getCollapsedLineCount();\n                        }\n                    }\n\n                    if (++topLine >= lineCount) {\n                        break;\n                    }\n                }\n            } else {\n                textAreaInsets = rTextArea.getInsets(textAreaInsets);\n\n                if (visibleRect.y < textAreaInsets.top) {\n                    visibleRect.height -= (textAreaInsets.top - visibleRect.y);\n                    visibleRect.y = textAreaInsets.top;\n                }\n\n                int topLine = (visibleRect.y - textAreaInsets.top) / cellHeight;\n                int actualTopY = topLine * cellHeight + textAreaInsets.top;\n                int y = actualTopY + ascent;\n\n                // Get the actual first line to paint, taking into account folding.\n                topLine += fm.getHiddenLineCountAbove(topLine, true);\n\n                // Paint line numbers\n                g.setColor(getForeground());\n\n                int line = topLine + 1;\n\n                while ((y < visibleRect.y + visibleRect.height + ascent) && (line <= rTextArea.getLineCount())) {\n                    paintLineNumber(g, metrics, rhs, y, line);\n\n                    y += cellHeight;\n\n                    if (fm != null) {\n                        Fold fold = fm.getFoldForLine(line - 1);\n                        // Skip to next line to paint, taking extra care for lines with\n                        // block ends and begins together, e.g. \"} else {\"\n                        while ((fold != null) && fold.isCollapsed()) {\n                            int hiddenLineCount = fold.getLineCount();\n                            if (hiddenLineCount == 0) {\n                                // Fold parser identified a 0-line fold region... This\n                                // is really a bug, but we'll handle it gracefully.\n                                break;\n                            }\n                            line += hiddenLineCount;\n                            fold = fm.getFoldForLine(line - 1);\n                        }\n                    }\n\n                    line++;\n                }\n            }\n        }\n\n        protected void paintLineNumber(Graphics g, FontMetrics metrics, int x, int y, int lineNumber) {\n            int originalLineNumber;\n\n            if (lineNumberMap != null) {\n                originalLineNumber = (lineNumber < lineNumberMap.length) ? lineNumberMap[lineNumber] : 0;\n            } else {\n                originalLineNumber = lineNumber;\n            }\n\n            if (originalLineNumber != 0) {\n                String number = Integer.toString(originalLineNumber);\n                int strWidth = metrics.stringWidth(number);\n                g.setColor(showMisalignment && (lineNumber != originalLineNumber) ? errorForeground : getForeground());\n                g.drawString(number, x-strWidth, y);\n            }\n        }\n\n        public int getRhsBorderWidth() { return ((RSyntaxTextArea)rTextArea).isCodeFoldingEnabled() ? 0 : 4; }\n\n        @Override\n        public Dimension getPreferredSize() {\n            if (preferredSize == null) {\n                int lineCount = getMaximumSourceLineNumber();\n\n                if (lineCount > 0) {\n                    Font font = getFont();\n                    FontMetrics fontMetrics = getFontMetrics(font);\n                    int count = 1;\n\n                    while (lineCount >= 10) {\n                        lineCount = lineCount / 10;\n                        count++;\n                    }\n\n                    int preferredWidth = fontMetrics.charWidth('9') * count + 10;\n                    preferredSize = new Dimension(preferredWidth, 0);\n                } else {\n                    preferredSize = new Dimension(0, 0);\n                }\n            }\n\n            return preferredSize;\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/view/component/DynamicPage.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view.component;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.*;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\n\nimport javax.swing.*;\nimport java.awt.*;\nimport java.io.ByteArrayInputStream;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.net.URI;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.concurrent.Future;\n\npublic class DynamicPage\n        extends JPanel\n        implements ContentCopyable, ContentSavable, ContentSearchable, ContentSelectable, FocusedTypeGettable,\n                   IndexesChangeListener, LineNumberNavigable, PreferencesChangeListener, UriGettable, UriOpenable,\n                   API.LoadSourceListener\n{\n    protected API api;\n    protected Container.Entry entry;\n    protected TypePage page;\n    protected URI lastOpenedUri;\n    protected Collection<Future<Indexes>> lastCollectionOfFutureIndexes;\n\n    public DynamicPage(API api, Container.Entry entry) {\n        super(new BorderLayout());\n        this.api = api;\n        this.entry = entry;\n\n        String source = api.getSource(entry);\n\n        if (source == null) {\n            // Display the decompiled source code\n            add(page = new ClassFilePage(api, entry));\n            // Try to load source in background\n            api.loadSource(entry, this);\n        } else {\n            // Display original source code\n            add(page = new JavaFilePage(api, new DelegatedEntry(entry, source)));\n        }\n    }\n\n    // --- ContentCopyable --- //\n    @Override public void copy() { page.copy(); }\n\n    // --- ContentSavable --- //\n    @Override public String getFileName() { return page.getFileName(); }\n    @Override public void save(API api, OutputStream outputStream) { page.save(api, outputStream); }\n\n    // --- ContentSearchable --- //\n    @Override public boolean highlightText(String text, boolean caseSensitive) { return page.highlightText(text, caseSensitive); }\n    @Override public void findNext(String text, boolean caseSensitive) { page.findNext(text, caseSensitive); }\n    @Override public void findPrevious(String text, boolean caseSensitive) { page.findPrevious(text, caseSensitive); }\n\n    // --- ContentSelectable --- //\n    @Override public void selectAll() { page.selectAll(); }\n\n    // --- FocusedTypeGettable --- //\n    @Override public String getFocusedTypeName() { return page.getFocusedTypeName(); }\n\n    // --- ContainerEntryGettable --- //\n    @Override public Container.Entry getEntry() { return entry; }\n\n    // --- IndexesChangeListener --- //\n    @Override public void indexesChanged(Collection<Future<Indexes>> collectionOfFutureIndexes) {\n        page.indexesChanged(lastCollectionOfFutureIndexes = collectionOfFutureIndexes);\n    }\n\n    // --- LineNumberNavigable --- //\n    @Override public int getMaximumLineNumber() { return page.getMaximumLineNumber(); }\n    @Override public void goToLineNumber(int lineNumber) { page.goToLineNumber(lineNumber); }\n    @Override public boolean checkLineNumber(int lineNumber) { return page.checkLineNumber(lineNumber); }\n\n    // --- PreferencesChangeListener --- //\n    @Override public void preferencesChanged(Map<String, String> preferences) { page.preferencesChanged(preferences); }\n\n    // --- UriGettable --- //\n    @Override public URI getUri() { return entry.getUri(); }\n\n    // --- UriOpenable --- //\n    @Override public boolean openUri(URI uri) { return page.openUri(lastOpenedUri = uri); }\n\n    // --- LoadSourceListener --- //\n    @Override public void sourceLoaded(String source) {\n        SwingUtilities.invokeLater(() -> {\n            // Replace the decompiled source code by the original\n            Point viewPosition = page.getScrollPane().getViewport().getViewPosition();\n\n            removeAll();\n            add(page = new JavaFilePage(api, new DelegatedEntry(entry, source)));\n            page.getScrollPane().getViewport().setViewPosition(viewPosition);\n\n            if (lastOpenedUri != null) {\n                page.openUri(lastOpenedUri);\n            }\n\n            if (lastCollectionOfFutureIndexes != null) {\n                page.indexesChanged(lastCollectionOfFutureIndexes);\n            }\n        });\n    }\n\n    protected static class DelegatedEntry implements Container.Entry {\n        protected Container.Entry entry;\n        protected String source;\n\n        DelegatedEntry(Container.Entry entry, String source) {\n            this.entry = entry;\n            this.source = source;\n        }\n\n        @Override public Container getContainer() { return entry.getContainer(); }\n        @Override public Container.Entry getParent() { return entry.getParent(); }\n        @Override public URI getUri() { return entry.getUri(); }\n        @Override public String getPath() { return entry.getPath(); }\n        @Override public boolean isDirectory() { return entry.isDirectory(); }\n        @Override public long length() { return entry.length(); }\n        @Override public InputStream getInputStream() { return new ByteArrayInputStream(source.getBytes()); }\n        @Override public Collection<Container.Entry> getChildren() { return entry.getChildren(); }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/view/component/EjbJarXmlFilePage.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view.component;\n\nimport org.fife.ui.rsyntaxtextarea.SyntaxConstants;\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.IndexesChangeListener;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.util.index.IndexesUtil;\nimport org.jd.gui.util.io.TextReader;\nimport org.jd.gui.util.xml.AbstractXmlPathFinder;\n\nimport java.awt.*;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.*;\nimport java.util.List;\nimport java.util.concurrent.Future;\n\npublic class EjbJarXmlFilePage extends TypeReferencePage implements UriGettable, IndexesChangeListener {\n    protected API api;\n    protected Container.Entry entry;\n    protected Collection<Future<Indexes>> collectionOfFutureIndexes = Collections.emptyList();\n\n    public EjbJarXmlFilePage(API api, Container.Entry entry) {\n        this.api = api;\n        this.entry = entry;\n        // Load content file\n        String text = TextReader.getText(entry.getInputStream());\n        // Create hyperlinks\n        new PathFinder().find(text);\n        // Display\n        setText(text);\n    }\n\n    public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_XML; }\n\n    protected boolean isHyperlinkEnabled(HyperlinkData hyperlinkData) { return ((TypeHyperlinkData)hyperlinkData).enabled; }\n\n    protected void openHyperlink(int x, int y, HyperlinkData hyperlinkData) {\n        TypeHyperlinkData data = (TypeHyperlinkData)hyperlinkData;\n\n        if (data.enabled) {\n            try {\n                // Save current position in history\n                Point location = textArea.getLocationOnScreen();\n                int offset = textArea.viewToModel(new Point(x - location.x, y - location.y));\n                URI uri = entry.getUri();\n                api.addURI(new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), \"position=\" + offset, null));\n\n                // Open link\n                String internalTypeName = data.internalTypeName;\n                List<Container.Entry> entries = IndexesUtil.findInternalTypeName(collectionOfFutureIndexes, internalTypeName);\n                String rootUri = entry.getContainer().getRoot().getUri().toString();\n                ArrayList<Container.Entry> sameContainerEntries = new ArrayList<>();\n\n                for (Container.Entry entry : entries) {\n                    if (entry.getUri().toString().startsWith(rootUri)) {\n                        sameContainerEntries.add(entry);\n                    }\n                }\n\n                if (sameContainerEntries.size() > 0) {\n                    api.openURI(x, y, sameContainerEntries, null, data.internalTypeName);\n                } else if (entries.size() > 0) {\n                    api.openURI(x, y, entries, null, data.internalTypeName);\n                }\n            } catch (URISyntaxException e) {\n                assert ExceptionUtil.printStackTrace(e);\n            }\n        }\n    }\n\n    // --- UriGettable --- //\n    public URI getUri() { return entry.getUri(); }\n\n    // --- ContentSavable --- //\n    public String getFileName() {\n        String path = entry.getPath();\n        int index = path.lastIndexOf('/');\n        return path.substring(index+1);\n    }\n\n    // --- IndexesChangeListener --- //\n    public void indexesChanged(Collection<Future<Indexes>> collectionOfFutureIndexes) {\n        // Update the list of containers\n        this.collectionOfFutureIndexes = collectionOfFutureIndexes;\n        // Refresh links\n        boolean refresh = false;\n\n        for (Map.Entry<Integer, HyperlinkData> entry : hyperlinks.entrySet()) {\n            TypeHyperlinkData entryData = (TypeHyperlinkData)entry.getValue();\n            String internalTypeName = entryData.internalTypeName;\n            boolean enabled = IndexesUtil.containsInternalTypeName(collectionOfFutureIndexes, internalTypeName);\n\n            if (entryData.enabled != enabled) {\n                entryData.enabled = enabled;\n                refresh = true;\n            }\n        }\n\n        if (refresh) {\n            textArea.repaint();\n        }\n    }\n\n    public static final List<String> typeHyperlinkPaths = Arrays.asList(\n        \"ejb-jar/assembly-descriptor/application-exception/exception-class\",\n        \"ejb-jar/assembly-descriptor/interceptor-binding/interceptor-class\",\n\n        \"ejb-jar/enterprise-beans/entity/home\",\n        \"ejb-jar/enterprise-beans/entity/remote\",\n        \"ejb-jar/enterprise-beans/entity/ejb-class\",\n        \"ejb-jar/enterprise-beans/entity/prim-key-class\",\n\n        \"ejb-jar/enterprise-beans/message-driven/ejb-class\",\n        \"ejb-jar/enterprise-beans/message-driven/messaging-type\",\n        \"ejb-jar/enterprise-beans/message-driven/resource-ref/injection-target/injection-target-class\",\n        \"ejb-jar/enterprise-beans/message-driven/resource-env-ref/injection-target/injection-target-class\",\n\n        \"ejb-jar/enterprise-beans/session/home\",\n        \"ejb-jar/enterprise-beans/session/local\",\n        \"ejb-jar/enterprise-beans/session/remote\",\n        \"ejb-jar/enterprise-beans/session/business-local\",\n        \"ejb-jar/enterprise-beans/session/business-remote\",\n        \"ejb-jar/enterprise-beans/session/service-endpoint\",\n        \"ejb-jar/enterprise-beans/session/ejb-class\",\n        \"ejb-jar/enterprise-beans/session/ejb-ref/home\",\n        \"ejb-jar/enterprise-beans/session/ejb-ref/remote\",\n\n        \"ejb-jar/interceptors/interceptor/around-invoke/class\",\n        \"ejb-jar/interceptors/interceptor/ejb-ref/home\",\n        \"ejb-jar/interceptors/interceptor/ejb-ref/remote\",\n        \"ejb-jar/interceptors/interceptor/interceptor-class\"\n    );\n\n    public class PathFinder extends AbstractXmlPathFinder {\n        public PathFinder() {\n            super(typeHyperlinkPaths);\n        }\n\n        public void handle(String path, String text, int position) {\n            String trim = text.trim();\n            if (trim != null) {\n                int startIndex = position + text.indexOf(trim);\n                int endIndex = startIndex + trim.length();\n                String internalTypeName = trim.replace(\".\", \"/\");\n                addHyperlink(new TypeHyperlinkData(startIndex, endIndex, internalTypeName));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/view/component/HyperlinkPage.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view.component;\n\nimport org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;\nimport org.fife.ui.rsyntaxtextarea.Token;\n\nimport java.awt.*;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.util.Map;\nimport java.util.TreeMap;\n\npublic abstract class HyperlinkPage extends TextPage {\n    protected static final Cursor DEFAULT_CURSOR = Cursor.getDefaultCursor();\n    protected static final Cursor HAND_CURSOR = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);\n\n    protected TreeMap<Integer, HyperlinkData> hyperlinks = new TreeMap<>();\n\n    public HyperlinkPage() {\n        MouseAdapter listener = new MouseAdapter() {\n            int lastX = -1;\n            int lastY = -1;\n            int lastModifiers = -1;\n\n            public void mouseClicked(MouseEvent e) {\n                if ((e.getClickCount() == 1) && ((e.getModifiers() & (Event.ALT_MASK|Event.META_MASK|Event.SHIFT_MASK)) == 0)) {\n                    int offset = textArea.viewToModel(new Point(e.getX(), e.getY()));\n                    if (offset != -1) {\n                        Map.Entry<Integer, HyperlinkData> entry = hyperlinks.floorEntry(offset);\n                        if (entry != null) {\n                            HyperlinkData entryData = entry.getValue();\n                            if ((entryData != null) && (offset < entryData.endPosition) && (offset >= entryData.startPosition) && isHyperlinkEnabled(entryData)) {\n                                openHyperlink(e.getXOnScreen(), e.getYOnScreen(), entryData);\n                            }\n                        }\n                    }\n                }\n            }\n\n            public void mouseMoved(MouseEvent e) {\n                if ((e.getX() != lastX) || (e.getY() != lastY) || (lastModifiers != e.getModifiers())) {\n                    lastX = e.getX();\n                    lastY = e.getY();\n                    lastModifiers = e.getModifiers();\n\n                    if ((e.getModifiers() & (Event.ALT_MASK|Event.META_MASK|Event.SHIFT_MASK)) == 0) {\n                        int offset = textArea.viewToModel(new Point(e.getX(), e.getY()));\n                        if (offset != -1) {\n                            Map.Entry<Integer, HyperlinkData> entry = hyperlinks.floorEntry(offset);\n                            if (entry != null) {\n                                HyperlinkData entryData = entry.getValue();\n                                if ((entryData != null) && (offset < entryData.endPosition) && (offset >= entryData.startPosition) && isHyperlinkEnabled(entryData)) {\n                                    if (textArea.getCursor() != HAND_CURSOR) {\n                                        textArea.setCursor(HAND_CURSOR);\n                                    }\n                                    return;\n                                }\n                            }\n                        }\n                    }\n\n                    if (textArea.getCursor() != DEFAULT_CURSOR) {\n                        textArea.setCursor(DEFAULT_CURSOR);\n                    }\n                }\n            }\n        };\n\n        textArea.addMouseListener(listener);\n        textArea.addMouseMotionListener(listener);\n    }\n\n    protected RSyntaxTextArea newSyntaxTextArea() { return new HyperlinkSyntaxTextArea(); }\n\n    public void addHyperlink(HyperlinkData hyperlinkData) {\n        hyperlinks.put(hyperlinkData.startPosition, hyperlinkData);\n    }\n\n    public void clearHyperlinks() {\n        hyperlinks.clear();\n    }\n\n    protected abstract boolean isHyperlinkEnabled(HyperlinkData hyperlinkData);\n\n    protected abstract void openHyperlink(int x, int y, HyperlinkData hyperlinkData);\n\n    public static class HyperlinkData {\n        public int startPosition;\n        public int endPosition;\n\n        public HyperlinkData(int startPosition, int endPosition) {\n            this.startPosition = startPosition;\n            this.endPosition = endPosition;\n        }\n    }\n\n    public class HyperlinkSyntaxTextArea extends RSyntaxTextArea {\n        /**\n         * @see HyperlinkPage.HyperlinkSyntaxTextArea#getUnderlineForToken(org.fife.ui.rsyntaxtextarea.Token)\n         */\n        @Override\n        public boolean getUnderlineForToken(Token t) {\n            Map.Entry<Integer, HyperlinkData> entry = hyperlinks.floorEntry(t.getOffset());\n            if (entry != null) {\n                HyperlinkData entryData = entry.getValue();\n                if ((entryData != null) && (t.getOffset() < entryData.endPosition) && (t.getOffset() >= entryData.startPosition) && isHyperlinkEnabled(entryData)) {\n                    return true;\n                }\n            }\n            return super.getUnderlineForToken(t);\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/view/component/JavaFilePage.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view.component;\n\nimport org.antlr.v4.runtime.ANTLRInputStream;\nimport org.antlr.v4.runtime.ParserRuleContext;\nimport org.antlr.v4.runtime.RuleContext;\nimport org.antlr.v4.runtime.Token;\nimport org.antlr.v4.runtime.tree.ParseTree;\nimport org.antlr.v4.runtime.tree.TerminalNode;\nimport org.fife.ui.rsyntaxtextarea.SyntaxConstants;\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.util.io.TextReader;\nimport org.jd.gui.util.parser.antlr.ANTLRJavaParser;\nimport org.jd.gui.util.parser.antlr.AbstractJavaListener;\nimport org.jd.gui.util.parser.antlr.JavaParser;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\npublic class JavaFilePage extends TypePage {\n\n    public JavaFilePage(API api, Container.Entry entry) {\n        super(api, entry);\n        // Load content file\n        String text = TextReader.getText(entry.getInputStream()).replace(\"\\r\\n\", \"\\n\").replace('\\r', '\\n');\n        // Parse\n        DeclarationListener declarationListener = new DeclarationListener(entry);\n        ReferenceListener referenceListener = new ReferenceListener(entry);\n\n        ANTLRJavaParser.parse(new ANTLRInputStream(text), declarationListener);\n        referenceListener.init(declarationListener);\n        ANTLRJavaParser.parse(new ANTLRInputStream(text), referenceListener);\n        // Display\n        setText(text);\n        initLineNumbers();\n    }\n\n    public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_JAVA; }\n\n    // --- ContentSavable --- //\n    public String getFileName() {\n        String path = entry.getPath();\n        int index = path.lastIndexOf('/');\n        return path.substring(index+1);\n    }\n\n    public class DeclarationListener extends AbstractJavaListener {\n        protected StringBuilder sbTypeDeclaration = new StringBuilder();\n        protected String currentInternalTypeName;\n\n        public DeclarationListener(Container.Entry entry) { super(entry); }\n\n        public HashMap<String, String> getNameToInternalTypeName() { return super.nameToInternalTypeName; }\n\n        // --- Add declarations --- //\n        public void enterPackageDeclaration(JavaParser.PackageDeclarationContext ctx) {\n            super.enterPackageDeclaration(ctx);\n\n            if (! packageName.isEmpty()) {\n                sbTypeDeclaration.append(packageName).append('/');\n            }\n        }\n\n        public void enterImportDeclaration(JavaParser.ImportDeclarationContext ctx) {\n            List<TerminalNode> identifiers = ctx.qualifiedName().Identifier();\n            String internalTypeName = concatIdentifiers(identifiers);\n            String typeName = identifiers.get(identifiers.size()-1).getSymbol().getText();\n\n            nameToInternalTypeName.put(typeName, internalTypeName);\n        }\n\n        public void enterClassDeclaration(JavaParser.ClassDeclarationContext ctx) { enterTypeDeclaration(ctx); }\n        public void exitClassDeclaration(JavaParser.ClassDeclarationContext ctx) { exitTypeDeclaration(); }\n\n        public void enterEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { enterTypeDeclaration(ctx); }\n        public void exitEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { exitTypeDeclaration(); }\n\n        public void enterInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { enterTypeDeclaration(ctx); }\n        public void exitInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { exitTypeDeclaration(); }\n\n        public void enterAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { enterTypeDeclaration(ctx); }\n        public void exitAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { exitTypeDeclaration(); }\n\n        public void enterTypeDeclaration(ParserRuleContext ctx) {\n            // Type declaration\n            TerminalNode identifier = ctx.getToken(JavaParser.Identifier, 0);\n            String typeName = identifier.getText();\n            int position = identifier.getSymbol().getStartIndex();\n            int length = sbTypeDeclaration.length();\n\n            if ((length == 0) || (sbTypeDeclaration.charAt(length-1) == '/')) {\n                sbTypeDeclaration.append(typeName);\n            } else {\n                sbTypeDeclaration.append('$').append(typeName);\n            }\n\n            currentInternalTypeName = sbTypeDeclaration.toString();\n            nameToInternalTypeName.put(typeName, currentInternalTypeName);\n\n            // Super type reference\n            JavaParser.TypeContext superType = ctx.getRuleContext(JavaParser.TypeContext.class, 0);\n            String superInternalTypeName = (superType != null) ? resolveInternalTypeName(superType.classOrInterfaceType().Identifier()) : null;\n            TypeDeclarationData data = new TypeDeclarationData(position, typeName.length(), currentInternalTypeName, null, null, superInternalTypeName);\n\n            declarations.put(currentInternalTypeName, data);\n            typeDeclarations.put(position, data);\n        }\n\n        public void exitTypeDeclaration() {\n            int index = sbTypeDeclaration.lastIndexOf(\"$\");\n\n            if (index == -1) {\n                index = sbTypeDeclaration.lastIndexOf(\"/\") + 1;\n            }\n\n            if (index == -1) {\n                sbTypeDeclaration.setLength(0);\n            } else {\n                sbTypeDeclaration.setLength(index);\n            }\n\n            currentInternalTypeName = sbTypeDeclaration.toString();\n        }\n\n        public void enterClassBodyDeclaration(JavaParser.ClassBodyDeclarationContext ctx) {\n            if (ctx.getChildCount() == 2) {\n                ParseTree first = ctx.getChild(0);\n\n                if (first instanceof TerminalNode) {\n                    TerminalNode f = (TerminalNode)first;\n\n                    if (f.getSymbol().getType() == JavaParser.STATIC) {\n                        String name = f.getText();\n                        int position = f.getSymbol().getStartIndex();\n                        declarations.put(currentInternalTypeName + \"-<clinit>-()V\", new TypePage.DeclarationData(position, 6, currentInternalTypeName, name, \"()V\"));\n                    }\n                }\n            }\n        }\n\n        public void enterConstDeclaration(JavaParser.ConstDeclarationContext ctx) {\n            JavaParser.TypeContext typeContext = ctx.type();\n\n            for (JavaParser.ConstantDeclaratorContext constantDeclaratorContext : ctx.constantDeclarator()) {\n                TerminalNode identifier = constantDeclaratorContext.Identifier();\n                String name = identifier.getText();\n                int dimensionOnVariable = countDimension(constantDeclaratorContext.children);\n                String descriptor = createDescriptor(typeContext, dimensionOnVariable);\n                int position = identifier.getSymbol().getStartIndex();\n\n                declarations.put(currentInternalTypeName + '-' + name + '-' + descriptor, new TypePage.DeclarationData(position, name.length(), currentInternalTypeName, name, descriptor));\n            }\n        }\n\n        public void enterFieldDeclaration(JavaParser.FieldDeclarationContext ctx) {\n            JavaParser.TypeContext typeContext = ctx.type();\n\n            for (JavaParser.VariableDeclaratorContext declaration : ctx.variableDeclarators().variableDeclarator()) {\n                JavaParser.VariableDeclaratorIdContext variableDeclaratorId = declaration.variableDeclaratorId();\n                TerminalNode identifier = variableDeclaratorId.Identifier();\n                String name = identifier.getText();\n                int dimensionOnVariable = countDimension(variableDeclaratorId.children);\n                String descriptor = createDescriptor(typeContext, dimensionOnVariable);\n                int position = identifier.getSymbol().getStartIndex();\n                TypePage.DeclarationData data = new TypePage.DeclarationData(position, name.length(), currentInternalTypeName, name, descriptor);\n\n                declarations.put(currentInternalTypeName + '-' + name + '-' + descriptor, data);\n            }\n        }\n\n        public void enterMethodDeclaration(JavaParser.MethodDeclarationContext ctx) {\n            enterMethodDeclaration(ctx, ctx.Identifier(), ctx.formalParameters(), ctx.type());\n        }\n\n        public void enterInterfaceMethodDeclaration(JavaParser.InterfaceMethodDeclarationContext ctx) {\n            enterMethodDeclaration(ctx, ctx.Identifier(), ctx.formalParameters(), ctx.type());\n        }\n\n        public void enterMethodDeclaration(\n                ParserRuleContext ctx, TerminalNode identifier,\n                JavaParser.FormalParametersContext formalParameters, JavaParser.TypeContext returnType) {\n\n            String name = identifier.getText();\n            String paramDescriptors = createParamDescriptors(formalParameters.formalParameterList());\n            String returnDescriptor = createDescriptor(returnType, 0);\n            String descriptor = paramDescriptors + returnDescriptor;\n            int position = identifier.getSymbol().getStartIndex();\n\n            declarations.put(currentInternalTypeName + '-' + name + '-' + descriptor, new TypePage.DeclarationData(position, name.length(), currentInternalTypeName, name, descriptor));\n        }\n\n        public void enterConstructorDeclaration(JavaParser.ConstructorDeclarationContext ctx) {\n            TerminalNode identifier = ctx.Identifier();\n            String name = identifier.getText();\n            String paramDescriptors = createParamDescriptors(ctx.formalParameters().formalParameterList());\n            String descriptor = paramDescriptors + \"V\";\n            int position = identifier.getSymbol().getStartIndex();\n\n            declarations.put(currentInternalTypeName + \"-<init>-\" + descriptor, new TypePage.DeclarationData(position, name.length(), currentInternalTypeName, name, descriptor));\n        }\n\n        public String createParamDescriptors(JavaParser.FormalParameterListContext formalParameterList) {\n            StringBuilder paramDescriptors = null;\n\n            if (formalParameterList != null) {\n                List<JavaParser.FormalParameterContext> formalParameters = formalParameterList.formalParameter();\n                paramDescriptors = new StringBuilder(\"(\");\n\n                for (JavaParser.FormalParameterContext formalParameter : formalParameters) {\n                    int dimensionOnParameter = countDimension(formalParameter.variableDeclaratorId().children);\n                    String descriptor = createDescriptor(formalParameter.type(), dimensionOnParameter);\n\n                    paramDescriptors.append(descriptor);\n                }\n            }\n\n            return (paramDescriptors == null) ? \"()\" : paramDescriptors.append(')').toString();\n        }\n    }\n\n    public class ReferenceListener extends AbstractJavaListener {\n        protected StringBuilder sbTypeDeclaration = new StringBuilder();\n        protected HashMap<String, TypePage.ReferenceData> referencesCache = new HashMap<>();\n        protected String currentInternalTypeName;\n        protected Context currentContext = null;\n\n        public ReferenceListener(Container.Entry entry) { super(entry); }\n\n        public void init(DeclarationListener declarationListener) {\n            this.nameToInternalTypeName.putAll(declarationListener.getNameToInternalTypeName());\n        }\n\n        // --- Add declarations --- //\n        public void enterPackageDeclaration(JavaParser.PackageDeclarationContext ctx) {\n            super.enterPackageDeclaration(ctx);\n\n            if (! packageName.isEmpty()) {\n                sbTypeDeclaration.append(packageName).append('/');\n            }\n        }\n\n        public void enterImportDeclaration(JavaParser.ImportDeclarationContext ctx) {\n            List<TerminalNode> identifiers = ctx.qualifiedName().Identifier();\n            int position = identifiers.get(0).getSymbol().getStartIndex();\n            String internalTypeName = concatIdentifiers(identifiers);\n\n            addHyperlink(new TypePage.HyperlinkReferenceData(position, internalTypeName.length(), newReferenceData(internalTypeName, null, null, null)));\n        }\n\n        public void enterClassDeclaration(JavaParser.ClassDeclarationContext ctx) { enterTypeDeclaration(ctx); }\n        public void exitClassDeclaration(JavaParser.ClassDeclarationContext ctx) { exitTypeDeclaration(); }\n\n        public void enterEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { enterTypeDeclaration(ctx); }\n        public void exitEnumDeclaration(JavaParser.EnumDeclarationContext ctx) { exitTypeDeclaration(); }\n\n        public void enterInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { enterTypeDeclaration(ctx); }\n        public void exitInterfaceDeclaration(JavaParser.InterfaceDeclarationContext ctx) { exitTypeDeclaration(); }\n\n        public void enterAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { enterTypeDeclaration(ctx); }\n        public void exitAnnotationTypeDeclaration(JavaParser.AnnotationTypeDeclarationContext ctx) { exitTypeDeclaration(); }\n\n        public void enterTypeDeclaration(ParserRuleContext ctx) {\n            // Type declaration\n            TerminalNode identifier = ctx.getToken(JavaParser.Identifier, 0);\n            String typeName = identifier.getText();\n            int length = sbTypeDeclaration.length();\n\n            if ((length == 0) || (sbTypeDeclaration.charAt(length-1) == '/')) {\n                sbTypeDeclaration.append(typeName);\n            } else {\n                sbTypeDeclaration.append('$').append(typeName);\n            }\n\n            currentInternalTypeName = sbTypeDeclaration.toString();\n            currentContext = new Context(currentContext);\n        }\n\n        public void exitTypeDeclaration() {\n            int index = sbTypeDeclaration.lastIndexOf(\"$\");\n\n            if (index == -1) {\n                index = sbTypeDeclaration.lastIndexOf(\"/\") + 1;\n            }\n\n            if (index == -1) {\n                sbTypeDeclaration.setLength(0);\n            } else {\n                sbTypeDeclaration.setLength(index);\n            }\n\n            currentInternalTypeName = sbTypeDeclaration.toString();\n        }\n\n        public void enterFormalParameters(JavaParser.FormalParametersContext ctx) {\n            JavaParser.FormalParameterListContext formalParameterList = ctx.formalParameterList();\n\n            if (formalParameterList != null) {\n                List<JavaParser.FormalParameterContext> formalParameters = formalParameterList.formalParameter();\n\n                for (JavaParser.FormalParameterContext formalParameter : formalParameters) {\n                    int dimensionOnParameter = countDimension(formalParameter.variableDeclaratorId().children);\n                    String descriptor = createDescriptor(formalParameter.type(), dimensionOnParameter);\n                    String name = formalParameter.variableDeclaratorId().Identifier().getSymbol().getText();\n\n                    currentContext.nameToDescriptor.put(name, descriptor);\n                }\n            }\n        }\n\n        // --- Add references --- //\n        public void enterType(JavaParser.TypeContext ctx) {\n            // Add type reference\n            JavaParser.ClassOrInterfaceTypeContext classOrInterfaceType = ctx.classOrInterfaceType();\n\n            if (classOrInterfaceType != null) {\n                List<TerminalNode> identifiers = classOrInterfaceType.Identifier();\n                String name = concatIdentifiers(identifiers);\n                String internalTypeName = resolveInternalTypeName(identifiers);\n                int position = identifiers.get(0).getSymbol().getStartIndex();\n\n                addHyperlink(new TypePage.HyperlinkReferenceData(position, name.length(), newReferenceData(internalTypeName, null, null, currentInternalTypeName)));\n            }\n        }\n\n        public void enterLocalVariableDeclaration(JavaParser.LocalVariableDeclarationContext ctx) {\n            JavaParser.TypeContext typeContext = ctx.type();\n\n            for (JavaParser.VariableDeclaratorContext variableDeclarator : ctx.variableDeclarators().variableDeclarator()) {\n                JavaParser.VariableDeclaratorIdContext variableDeclaratorId = variableDeclarator.variableDeclaratorId();\n                int dimensionOnVariable = countDimension(variableDeclaratorId.children);\n                String descriptor = createDescriptor(typeContext, dimensionOnVariable);\n                String name = variableDeclarator.variableDeclaratorId().Identifier().getSymbol().getText();\n\n                currentContext.nameToDescriptor.put(name, descriptor);\n            }\n        }\n\n        public void enterCreator(JavaParser.CreatorContext ctx) {\n            enterNewExpression(ctx.createdName().Identifier(), ctx.classCreatorRest());\n        }\n\n        public void enterInnerCreator(JavaParser.InnerCreatorContext ctx) {\n            enterNewExpression(Collections.singletonList(ctx.Identifier()), ctx.classCreatorRest());\n        }\n\n        public void enterNewExpression(List<TerminalNode> identifiers, JavaParser.ClassCreatorRestContext classCreatorRest) {\n            if (identifiers.size() > 0) {\n                String name = concatIdentifiers(identifiers);\n                String internalTypeName = resolveInternalTypeName(identifiers);\n                int position = identifiers.get(0).getSymbol().getStartIndex();\n\n                if (classCreatorRest != null) {\n                    // Constructor call -> Add a link to the constructor declaration\n                    JavaParser.ExpressionListContext expressionList = classCreatorRest.arguments().expressionList();\n                    String descriptor = (expressionList != null) ? getParametersDescriptor(expressionList).append('V').toString() : \"()V\";\n\n                    addHyperlink(new TypePage.HyperlinkReferenceData(position, name.length(), newReferenceData(internalTypeName, \"<init>\", descriptor, currentInternalTypeName)));\n                } else {\n                    // New type array -> Add a link to the type declaration\n                    addHyperlink(new TypePage.HyperlinkReferenceData(position, name.length(), newReferenceData(internalTypeName, null, null, currentInternalTypeName)));\n                }\n            }\n        }\n\n        public void enterExpression(JavaParser.ExpressionContext ctx) {\n            switch (ctx.getChildCount()) {\n                case 1:\n                    TerminalNode identifier0 = getToken(ctx.children, JavaParser.Identifier, 0);\n\n                    if (identifier0 != null) {\n                        if (isAField(ctx)) {\n                            JavaParser.PrimaryContext primaryContext = ctx.primary();\n\n                            if (primaryContext != null) {\n                                String fieldName = primaryContext.literal().StringLiteral().getText();\n\n                                if (currentContext.getDescriptor(fieldName) == null) {\n                                    // Not a local variable or a method parameter\n                                    String fieldTypeName = searchInternalTypeNameForThisFieldName(currentInternalTypeName, fieldName);\n                                    int position = ctx.Identifier().getSymbol().getStartIndex();\n\n                                    addHyperlink(new TypePage.HyperlinkReferenceData(position, fieldName.length(), newReferenceData(fieldTypeName, fieldName, \"?\", currentInternalTypeName)));\n                                }\n                            }\n                        }\n                    } else if (ctx.primary() != null) {\n                        TerminalNode identifier = ctx.primary().Identifier();\n\n                        if (identifier != null) {\n                            Token symbol = identifier.getSymbol();\n                            String name = symbol.getText();\n                            String internalTypeName = nameToInternalTypeName.get(name);\n\n                            if (internalTypeName != null) {\n                                int position = symbol.getStartIndex();\n\n                                addHyperlink(new TypePage.HyperlinkReferenceData(position, name.length(), newReferenceData(internalTypeName, null, null, currentInternalTypeName)));\n                            }\n                        }\n                    }\n                    break;\n                case 3:\n                    if (getToken(ctx.children, JavaParser.DOT, 1) != null) {\n                        // Search \"expression '.' Identifier\" : field reference\n                        TerminalNode identifier3 = getToken(ctx.children, JavaParser.Identifier, 2);\n\n                        if ((identifier3 != null) && isAField(ctx)) {\n                            String fieldTypeName = getInternalTypeName(ctx.getChild(0));\n\n                            if (fieldTypeName != null) {\n                                int position = identifier3.getSymbol().getStartIndex();\n                                String fieldName = identifier3.getText();\n\n                                addHyperlink(new TypePage.HyperlinkReferenceData(position, fieldName.length(), newReferenceData(fieldTypeName, fieldName, \"?\", currentInternalTypeName)));\n                            }\n                        }\n                    } else if (getToken(ctx.children, JavaParser.LPAREN, 1) != null) {\n                        // Search \"expression '(' ')'\" : method reference\n                        if (getToken(ctx.children, JavaParser.RPAREN, 2) != null) {\n                            enterCallMethodExpression(ctx, null);\n                        }\n                    }\n                    break;\n                case 4:\n                    if (getToken(ctx.children, JavaParser.LPAREN, 1) != null) {\n                        // Search \"expression '(' expressionList ')'\" : method reference\n                        if (getToken(ctx.children, JavaParser.RPAREN, 3) != null) {\n                            JavaParser.ExpressionListContext expressionListContext = ctx.expressionList();\n\n                            if ((expressionListContext != null) && (expressionListContext == ctx.children.get(2))) {\n                                enterCallMethodExpression(ctx, expressionListContext);\n                            }\n                        }\n                    }\n                    break;\n            }\n        }\n\n        public void enterCallMethodExpression(JavaParser.ExpressionContext ctx, JavaParser.ExpressionListContext expressionListContext) {\n            ParseTree first = ctx.children.get(0);\n\n            if (first instanceof JavaParser.ExpressionContext) {\n                JavaParser.ExpressionContext f = (JavaParser.ExpressionContext)first;\n\n                switch (f.getChildCount()) {\n                    case 1:\n                        JavaParser.PrimaryContext primary = f.primary();\n                        TerminalNode identifier = primary.Identifier();\n\n                        if (identifier != null) {\n                            Token symbol = identifier.getSymbol();\n\n                            if (symbol != null) {\n                                String methodName = symbol.getText();\n                                String methodTypeName = searchInternalTypeNameForThisMethodName(currentInternalTypeName, methodName);\n\n                                if (methodTypeName != null) {\n                                    int position = symbol.getStartIndex();\n                                    String methodDescriptor = (expressionListContext != null) ? getParametersDescriptor(expressionListContext).append('?').toString() : \"()?\";\n\n                                    addHyperlink(new TypePage.HyperlinkReferenceData(position, methodName.length(), newReferenceData(methodTypeName, methodName, methodDescriptor, currentInternalTypeName)));\n                                }\n                            }\n                        } else {\n                            Token symbol = primary.getChild(TerminalNode.class, 0).getSymbol();\n\n                            if (symbol != null) {\n                                switch (symbol.getType()) {\n                                    case JavaParser.THIS:\n                                        int position = symbol.getStartIndex();\n                                        String methodDescriptor = (expressionListContext != null) ? getParametersDescriptor(expressionListContext).append('?').toString() : \"()?\";\n\n                                        addHyperlink(new TypePage.HyperlinkReferenceData(position, 4, newReferenceData(currentInternalTypeName, \"<init>\", methodDescriptor, currentInternalTypeName)));\n                                        break;\n                                    case JavaParser.SUPER:\n                                        DeclarationData data = declarations.get(currentInternalTypeName);\n\n                                        if (data instanceof TypeDeclarationData) {\n                                            position = symbol.getStartIndex();\n                                            String methodTypeName = ((TypeDeclarationData) data).superTypeName;\n                                            methodDescriptor = (expressionListContext != null) ? getParametersDescriptor(expressionListContext).append('?').toString() : \"()?\";\n\n                                            addHyperlink(new TypePage.HyperlinkReferenceData(position, 5, newReferenceData(methodTypeName, \"<init>\", methodDescriptor, currentInternalTypeName)));\n                                        }\n                                        break;\n                                }\n                            }\n                        }\n                        break;\n                    case 3:\n                        // Search \"expression '.' Identifier\"\n                        ParseTree dot = first.getChild(1);\n\n                        if ((dot instanceof TerminalNode) && (((TerminalNode)dot).getSymbol().getType() == JavaParser.DOT)) {\n                            ParseTree identifier3 = first.getChild(2);\n\n                            if (identifier3 instanceof TerminalNode) {\n                                TerminalNode i3 = (TerminalNode)identifier3;\n\n                                if (i3.getSymbol().getType() == JavaParser.Identifier) {\n                                    String methodTypeName = getInternalTypeName(first.getChild(0));\n\n                                    if (methodTypeName != null) {\n                                        int position = i3.getSymbol().getStartIndex();\n                                        String methodName = i3.getText();\n                                        String methodDescriptor = (expressionListContext != null) ? getParametersDescriptor(expressionListContext).append('?').toString() : \"()?\";\n\n                                        addHyperlink(new TypePage.HyperlinkReferenceData(position, methodName.length(), newReferenceData(methodTypeName, methodName, methodDescriptor, currentInternalTypeName)));\n                                    }\n                                }\n                            }\n                        }\n                        break;\n                }\n            }\n        }\n\n        public StringBuilder getParametersDescriptor(JavaParser.ExpressionListContext expressionListContext) {\n            StringBuilder sb = new StringBuilder('(');\n            for (JavaParser.ExpressionContext exp : expressionListContext.expression()) sb.append('?');\n            sb.append(')');\n            return sb;\n        }\n\n        public boolean isAField(JavaParser.ExpressionContext ctx) {\n            RuleContext parent = ctx.parent;\n\n            if (parent instanceof JavaParser.ExpressionContext) {\n                int size = parent.getChildCount();\n\n                if (parent.getChild(size - 1) != ctx) {\n                    for (int i=0; i<size; i++) {\n                        if (parent.getChild(i) == ctx) {\n                            ParseTree next = parent.getChild(i+1);\n\n                            if (next instanceof TerminalNode) {\n                                switch (((TerminalNode)next).getSymbol().getType()) {\n                                    case JavaParser.DOT:\n                                    case JavaParser.LPAREN:\n                                        return false;\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n\n            return true;\n        }\n\n        public String getInternalTypeName(ParseTree pt) {\n            if (pt instanceof JavaParser.ExpressionContext) {\n\n                if (pt.getChildCount() == 1) {\n                    JavaParser.PrimaryContext primary = ((JavaParser.ExpressionContext)pt).primary();\n                    TerminalNode identifier = primary.Identifier();\n\n                    if (identifier != null) {\n                        String name = identifier.getSymbol().getText();\n                        String descriptor = (currentContext == null) ? null : currentContext.getDescriptor(name);\n\n                        if (descriptor != null) {\n                            // Is a local variable or a method parameter\n                            if (descriptor.charAt(0) == 'L') {\n                                return descriptor.substring(1, descriptor.length() - 1);\n                            }\n                        } else if (currentInternalTypeName != null) {\n                            String internalTypeName = searchInternalTypeNameForThisFieldName(currentInternalTypeName, name);\n\n                            if (internalTypeName != null) {\n                                // Is a field\n                                return internalTypeName;\n                            } else {\n                                internalTypeName = resolveInternalTypeName(Collections.singletonList(identifier));\n\n                                if (internalTypeName != null) {\n                                    // Is a type\n                                    return internalTypeName;\n                                } else {\n                                    // Not found\n                                    return null;\n                                }\n                            }\n                        }\n                    } else {\n                        TerminalNode tn = primary.getChild(TerminalNode.class, 0);\n                        Token symbol = (tn == null) ? null : tn.getSymbol();\n\n                        if (symbol != null) {\n                            switch (symbol.getType()) {\n                                case JavaParser.THIS:\n                                    return currentInternalTypeName;\n                                case JavaParser.SUPER:\n                                    DeclarationData data = declarations.get(currentInternalTypeName);\n\n                                    if (data instanceof TypeDeclarationData) {\n                                        return ((TypeDeclarationData)data).superTypeName;\n                                    } else {\n                                        return null;\n                                    }\n                            }\n                        }\n                    }\n                }\n            }\n\n            return null;\n        }\n\n        public String searchInternalTypeNameForThisFieldName(String internalTypeName, String name) {\n            String prefix = internalTypeName + '-' + name + '-';\n            int length = prefix.length();\n\n            for (Map.Entry<String, DeclarationData> entry : declarations.entrySet()) {\n                if (entry.getKey().startsWith(prefix) && (entry.getKey().charAt(length) != '(')) {\n                    return entry.getValue().typeName;\n                }\n            }\n\n            // Not found\n            int index = internalTypeName.lastIndexOf('$');\n\n            if (index != -1) {\n                // Search in the outer type\n                internalTypeName = internalTypeName.substring(0, index);\n\n                return searchInternalTypeNameForThisFieldName(internalTypeName, name);\n            }\n\n            // Not found\n            return null;\n        }\n\n        public String searchInternalTypeNameForThisMethodName(String internalTypeName, String name) {\n            String prefix = internalTypeName + '-' + name + \"-(\";\n\n            for (Map.Entry<String, DeclarationData> entry : declarations.entrySet()) {\n                if (entry.getKey().startsWith(prefix)) {\n                    return entry.getValue().typeName;\n                }\n            }\n\n            // Not found\n            int index = internalTypeName.lastIndexOf('$');\n\n            if (index != -1) {\n                // Search in the outer type\n                internalTypeName = internalTypeName.substring(0, index);\n\n                return searchInternalTypeNameForThisMethodName(internalTypeName, name);\n            }\n\n            // Not found\n            return null;\n        }\n\n        public TerminalNode getToken(List<ParseTree> children, int type, int i) {\n            ParseTree pt = children.get(i);\n\n            if (pt instanceof TerminalNode) {\n                if (((TerminalNode)pt).getSymbol().getType() == type) {\n                    return (TerminalNode)pt;\n                }\n            }\n\n            return null;\n        }\n\n        public void enterBlock(JavaParser.BlockContext ctx) {\n            currentContext = new Context(currentContext);\n        }\n\n        public void exitBlock(JavaParser.BlockContext ctx) {\n            currentContext = currentContext.outerContext;\n        }\n\n        public TypePage.ReferenceData newReferenceData(String internalName, String name, String descriptor, String scopeInternalName) {\n            String key = internalName + '-' + name + '-'+ descriptor + '-' + scopeInternalName;\n            TypePage.ReferenceData reference = referencesCache.get(key);\n\n            if (reference == null) {\n                reference = new TypePage.ReferenceData(internalName, name, descriptor, scopeInternalName);\n                referencesCache.put(key, reference);\n                references.add(reference);\n            }\n\n            return reference;\n        }\n\n        // --- Add strings --- //\n        public void enterLiteral(JavaParser.LiteralContext ctx) {\n            TerminalNode stringLiteral = ctx.StringLiteral();\n\n            if (stringLiteral != null) {\n                String str = stringLiteral.getSymbol().getText();\n                int position = stringLiteral.getSymbol().getStartIndex();\n\n                strings.add(new TypePage.StringData(position, str.length(), str, currentInternalTypeName));\n            }\n        }\n    }\n\n    public static class Context {\n        protected Context outerContext;\n\n        protected HashMap<String, String> nameToDescriptor = new HashMap<>();\n\n        public Context(Context outerContext) {\n            this.outerContext = outerContext;\n        }\n\n        /**\n         * @param name Parameter or variable name\n         * @return Qualified type name\n         */\n        public String getDescriptor(String name) {\n            String descriptor = nameToDescriptor.get(name);\n\n            if ((descriptor == null) && (outerContext != null)) {\n                descriptor = outerContext.getDescriptor(name);\n            }\n\n            return descriptor;\n        }\n    }\n\n    public static class TypeDeclarationData extends TypePage.DeclarationData {\n        protected String superTypeName;\n\n        public TypeDeclarationData(int startPosition, int length, String type, String name, String descriptor, String superTypeName) {\n            super(startPosition, length, type, name, descriptor);\n\n            this.superTypeName = superTypeName;\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/view/component/LogPage.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view.component;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.IndexesChangeListener;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.util.index.IndexesUtil;\n\nimport java.awt.*;\nimport java.net.URI;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.Future;\n\npublic class LogPage extends HyperlinkPage implements UriGettable, IndexesChangeListener {\n    protected API api;\n    protected URI uri;\n    protected Collection<Future<Indexes>> collectionOfFutureIndexes = Collections.emptyList();\n\n    public LogPage(API api, URI uri, String content) {\n        this.api = api;\n        this.uri = uri;\n        // Parse\n        int index = 0;\n        int eol = content.indexOf('\\n');\n\n        while (eol != -1) {\n            parseLine(content, index, eol);\n            index = eol + 1;\n            eol = content.indexOf('\\n', index);\n        }\n\n        parseLine(content, index, content.length());\n        // Display\n        setText(content);\n    }\n\n    protected void parseLine(String content, int index, int eol) {\n        int start = content.indexOf(\"at \", index);\n\n        if ((start != -1) && (start < eol)) {\n            int leftParenthesisIndex = content.indexOf('(', start);\n\n            if ((leftParenthesisIndex != -1) && (leftParenthesisIndex < eol)) {\n                addHyperlink(new LogHyperlinkData(start+3, leftParenthesisIndex));\n            }\n        }\n    }\n\n    protected boolean isHyperlinkEnabled(HyperlinkData hyperlinkData) { return ((LogHyperlinkData)hyperlinkData).enabled; }\n\n    protected void openHyperlink(int x, int y, HyperlinkData hyperlinkData) {\n        LogHyperlinkData logHyperlinkData = (LogHyperlinkData)hyperlinkData;\n\n        if (logHyperlinkData.enabled) {\n            try {\n                // Save current position in history\n                Point location = textArea.getLocationOnScreen();\n                int offset = textArea.viewToModel(new Point(x - location.x, y - location.y));\n                api.addURI(new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), \"position=\" + offset, null));\n\n                // Open link\n                String text = getText();\n                String typeAndMethodNames = text.substring(hyperlinkData.startPosition, hyperlinkData.endPosition);\n                int lastDotIndex = typeAndMethodNames.lastIndexOf('.');\n                String methodName = typeAndMethodNames.substring(lastDotIndex + 1);\n                String internalTypeName = typeAndMethodNames.substring(0, lastDotIndex).replace('.', '/');\n                List<Container.Entry> entries = IndexesUtil.findInternalTypeName(collectionOfFutureIndexes, internalTypeName);\n                int leftParenthesisIndex = hyperlinkData.endPosition + 1;\n                int rightParenthesisIndex = text.indexOf(')', leftParenthesisIndex);\n                String lineNumberOrNativeMethodFlag = text.substring(leftParenthesisIndex, rightParenthesisIndex);\n\n                if (lineNumberOrNativeMethodFlag.equals(\"Native Method\")) {\n                    // Example: at java.security.AccessController.doPrivileged(Native Method)\n                    lastDotIndex = internalTypeName.lastIndexOf('/');\n                    String shortTypeName = internalTypeName.substring(lastDotIndex + 1);\n                    api.openURI(x, y, entries, null, shortTypeName + '-' + methodName + \"-(*)?\");\n                } else {\n                    // Example: at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)\n                    int colonIndex = lineNumberOrNativeMethodFlag.indexOf(':');\n                    String lineNumber = lineNumberOrNativeMethodFlag.substring(colonIndex + 1);\n                    api.openURI(x, y, entries, \"lineNumber=\" + lineNumber, null);\n                }\n            } catch (Exception e) {\n                assert ExceptionUtil.printStackTrace(e);\n            }\n        }\n    }\n\n    // --- UriGettable --- //\n    public URI getUri() { return uri; }\n\n    // --- ContentSavable --- //\n    public String getFileName() {\n        String path = uri.getPath();\n        int index = path.lastIndexOf('/');\n        return path.substring(index + 1);\n    }\n\n    // --- IndexesChangeListener --- //\n    public void indexesChanged(Collection<Future<Indexes>> collectionOfFutureIndexes) {\n        // Update the list of containers\n        this.collectionOfFutureIndexes = collectionOfFutureIndexes;\n        // Refresh links\n        boolean refresh = false;\n        String text = getText();\n\n        for (Map.Entry<Integer, HyperlinkData> entry : hyperlinks.entrySet()) {\n            LogHyperlinkData entryData = (LogHyperlinkData)entry.getValue();\n            String typeAndMethodNames = text.substring(entryData.startPosition, entryData.endPosition);\n            int lastDotIndex = typeAndMethodNames.lastIndexOf('.');\n            String internalTypeName = typeAndMethodNames.substring(0, lastDotIndex).replace('.', '/');\n            boolean enabled = IndexesUtil.containsInternalTypeName(collectionOfFutureIndexes, internalTypeName);\n\n            if (entryData.enabled != enabled) {\n                entryData.enabled = enabled;\n                refresh = true;\n            }\n        }\n\n        if (refresh) {\n            textArea.repaint();\n        }\n    }\n\n    public static class LogHyperlinkData extends HyperlinkData {\n        public boolean enabled = false;\n\n        public LogHyperlinkData(int startPosition, int endPosition) {\n            super(startPosition, endPosition);\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/view/component/ManifestFilePage.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view.component;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.IndexesChangeListener;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.util.index.IndexesUtil;\nimport org.jd.gui.util.io.TextReader;\n\nimport java.awt.*;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.*;\nimport java.util.List;\nimport java.util.concurrent.Future;\n\npublic class ManifestFilePage extends HyperlinkPage implements UriGettable, IndexesChangeListener {\n    protected API api;\n    protected Container.Entry entry;\n    protected Collection<Future<Indexes>> collectionOfFutureIndexes = Collections.emptyList();\n\n    public ManifestFilePage(API api, Container.Entry entry) {\n        this.api = api;\n        this.entry = entry;\n        // Load content file\n        String text = TextReader.getText(entry.getInputStream());\n        // Parse hyperlinks. Docs:\n        // - http://docs.oracle.com/javase/7/docs/technotes/guides/jar/jar.html\n        // - http://docs.oracle.com/javase/7/docs/api/java/lang/instrument/package-summary.html\n        int startLineIndex = text.indexOf(\"Main-Class:\");\n        if (startLineIndex != -1) {\n            // Example: Main-Class: jd.gui.App\n            int startIndex = skipSeparators(text, startLineIndex + \"Main-Class:\".length());\n            int endIndex = searchEndIndexOfValue(text, startLineIndex, startIndex);\n            String typeName = text.substring(startIndex, endIndex);\n            String internalTypeName = typeName.replace('.', '/');\n            addHyperlink(new ManifestHyperlinkData(startIndex, endIndex, internalTypeName + \"-main-([Ljava/lang/String;)V\"));\n        }\n\n        startLineIndex = text.indexOf(\"Premain-Class:\");\n        if (startLineIndex != -1) {\n            // Example: Premain-Class: packge.JavaAgent\n            int startIndex = skipSeparators(text, startLineIndex + \"Premain-Class:\".length());\n            int endIndex = searchEndIndexOfValue(text, startLineIndex, startIndex);\n            String typeName = text.substring(startIndex, endIndex);\n            String internalTypeName = typeName.replace('.', '/');\n            // Undefined parameters : 2 candidate methods\n            // http://docs.oracle.com/javase/6/docs/api/java/lang/instrument/package-summary.html\n            addHyperlink(new ManifestHyperlinkData(startIndex, endIndex, internalTypeName + \"-premain-(*)?\"));\n        }\n        // Display\n        setText(text);\n    }\n\n    public int skipSeparators(String text, int index) {\n        int length = text.length();\n\n        while (index < length) {\n            switch (text.charAt(index)) {\n                case ' ': case '\\t': case '\\n': case '\\r':\n                    index++;\n                    break;\n                default:\n                    return index;\n            }\n        }\n\n        return index;\n    }\n\n    public int searchEndIndexOfValue(String text, int startLineIndex, int startIndex) {\n        int length = text.length();\n        int index = startIndex;\n\n        while (index < length) {\n            // MANIFEST.MF Specification: max line length = 72\n            // http://docs.oracle.com/javase/7/docs/technotes/guides/jar/jar.html\n            switch (text.charAt(index)) {\n                case '\\r':\n                    // CR followed by LF ?\n                    if ((index-startLineIndex >= 70) && (index+1 < length) && (text.charAt(index+1) == ' ')) {\n                        // Multiline value\n                        startLineIndex = index+1;\n                    } else if ((index-startLineIndex >= 70) && (index+2 < length) && (text.charAt(index+1) == '\\n') && (text.charAt(index+2) == ' ')) {\n                        // Multiline value\n                        index++;\n                        startLineIndex = index+1;\n                    } else {\n                        // (End of file) or (single line value) => return end index\n                        return index;\n                    }\n                    break;\n                case '\\n':\n                    if ((index-startLineIndex >= 70) && (index+1 < length) && (text.charAt(index+1) == ' ')) {\n                        // Multiline value\n                        startLineIndex = index+1;\n                    } else {\n                        // (End of file) or (single line value) => return end index\n                        return index;\n                    }\n                    break;\n            }\n            index++;\n        }\n\n        return index;\n    }\n\n    protected boolean isHyperlinkEnabled(HyperlinkData hyperlinkData) { return ((ManifestHyperlinkData)hyperlinkData).enabled; }\n\n    protected void openHyperlink(int x, int y, HyperlinkData hyperlinkData) {\n        ManifestHyperlinkData data = (ManifestHyperlinkData)hyperlinkData;\n\n        if (data.enabled) {\n            try {\n                // Save current position in history\n                Point location = textArea.getLocationOnScreen();\n                int offset = textArea.viewToModel(new Point(x-location.x, y-location.y));\n                URI uri = entry.getUri();\n                api.addURI(new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), \"position=\" + offset, null));\n                // Open link\n                String text = getText();\n                String textLink = getValue(text, hyperlinkData.startPosition, hyperlinkData.endPosition);\n                String internalTypeName = textLink.replace('.', '/');\n                List<Container.Entry> entries = IndexesUtil.findInternalTypeName(collectionOfFutureIndexes, internalTypeName);\n                String rootUri = entry.getContainer().getRoot().getUri().toString();\n                ArrayList<Container.Entry> sameContainerEntries = new ArrayList<>();\n\n                for (Container.Entry entry : entries) {\n                    if (entry.getUri().toString().startsWith(rootUri)) {\n                        sameContainerEntries.add(entry);\n                    }\n                }\n\n                if (sameContainerEntries.size() > 0) {\n                    api.openURI(x, y, sameContainerEntries, null, data.fragment);\n                } else if (entries.size() > 0) {\n                    api.openURI(x, y, entries, null, data.fragment);\n                }\n            } catch (URISyntaxException e) {\n                assert ExceptionUtil.printStackTrace(e);\n            }\n        }\n    }\n\n    // --- UriGettable --- //\n    public URI getUri() { return entry.getUri(); }\n\n    // --- ContentSavable --- //\n    public String getFileName() {\n        String path = entry.getPath();\n        int index = path.lastIndexOf('/');\n        return path.substring(index+1);\n    }\n\n    // --- IndexesChangeListener --- //\n    public void indexesChanged(Collection<Future<Indexes>> collectionOfFutureIndexes) {\n        // Update the list of containers\n        this.collectionOfFutureIndexes = collectionOfFutureIndexes;\n        // Refresh links\n        boolean refresh = false;\n        String text = getText();\n\n        for (Map.Entry<Integer, HyperlinkData> entry : hyperlinks.entrySet()) {\n            ManifestHyperlinkData entryData = (ManifestHyperlinkData)entry.getValue();\n            String textLink = getValue(text, entryData.startPosition, entryData.endPosition);\n            String internalTypeName = textLink.replace('.', '/');\n            boolean enabled = IndexesUtil.containsInternalTypeName(collectionOfFutureIndexes, internalTypeName);\n\n            if (entryData.enabled != enabled) {\n                entryData.enabled = enabled;\n                refresh = true;\n            }\n        }\n\n        if (refresh) {\n            textArea.repaint();\n        }\n    }\n\n    public static String getValue(String text, int startPosition, int endPosition) {\n        return text\n            // Extract text of link\n            .substring(startPosition, endPosition)\n            // Convert multiline value\n            .replace(\"\\r\\n \", \"\")\n            .replace(\"\\r \", \"\")\n            .replace(\"\\n \", \"\");\n    }\n\n    public static class ManifestHyperlinkData extends HyperlinkData {\n        public boolean enabled;\n        public String fragment;\n\n        ManifestHyperlinkData(int startPosition, int endPosition, String fragment) {\n            super(startPosition, endPosition);\n            this.enabled = false;\n            this.fragment = fragment;\n        }\n    }\n}\n\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/view/component/ModuleInfoFilePage.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view.component;\n\nimport org.fife.ui.rsyntaxtextarea.*;\nimport org.fife.ui.rtextarea.Marker;\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.util.decompiler.ContainerLoader;\nimport org.jd.gui.util.decompiler.StringBuilderPrinter;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.util.index.IndexesUtil;\n\nimport javax.swing.text.Segment;\nimport java.awt.*;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.*;\nimport java.util.List;\nimport java.util.concurrent.Future;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport static org.jd.core.v1.api.printer.Printer.MODULE;\nimport static org.jd.core.v1.api.printer.Printer.PACKAGE;\nimport static org.jd.core.v1.api.printer.Printer.TYPE;\n\npublic class ModuleInfoFilePage extends ClassFilePage {\n    public static final String SYNTAX_STYLE_JAVA_MODULE = \"text/java-module\";\n\n    static {\n        // Add a new token maker for Java 9+ module\n        AbstractTokenMakerFactory atmf = (AbstractTokenMakerFactory)TokenMakerFactory.getDefaultInstance();\n        atmf.putMapping(SYNTAX_STYLE_JAVA_MODULE, ModuleInfoTokenMaker.class.getName());\n    }\n\n    public ModuleInfoFilePage(API api, Container.Entry entry) {\n        super(api, entry);\n    }\n\n    @Override\n    public void decompile(Map<String, String> preferences) {\n        try {\n            // Clear ...\n            clearHyperlinks();\n            clearLineNumbers();\n            typeDeclarations.clear();\n\n            // Init preferences\n            boolean unicodeEscape = getPreferenceValue(preferences, ESCAPE_UNICODE_CHARACTERS, false);\n\n            // Init loader\n            ContainerLoader loader = new ContainerLoader(entry);\n\n            // Init printer\n            ModuleInfoFilePrinter printer = new ModuleInfoFilePrinter();\n            printer.setUnicodeEscape(unicodeEscape);\n\n            // Format internal name\n            String entryPath = entry.getPath();\n            assert entryPath.endsWith(\".class\");\n            String entryInternalName = entryPath.substring(0, entryPath.length() - 6); // 6 = \".class\".length()\n\n            // Decompile class file\n            DECOMPILER.decompile(loader, printer, entryInternalName);\n        } catch (Throwable t) {\n            assert ExceptionUtil.printStackTrace(t);\n            setText(\"// INTERNAL ERROR //\");\n        }\n    }\n\n    @Override\n    public String getSyntaxStyle() { return SYNTAX_STYLE_JAVA_MODULE; }\n\n    @Override\n    protected void openHyperlink(int x, int y, HyperlinkData hyperlinkData) {\n        HyperlinkReferenceData hyperlinkReferenceData = (HyperlinkReferenceData)hyperlinkData;\n\n        if (hyperlinkReferenceData.reference.enabled) {\n            try {\n                // Save current position in history\n                Point location = textArea.getLocationOnScreen();\n                int offset = textArea.viewToModel(new Point(x - location.x, y - location.y));\n                URI uri = entry.getUri();\n                api.addURI(new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), \"position=\" + offset, null));\n\n                // Open link\n                ModuleInfoReferenceData moduleInfoReferenceData = (ModuleInfoReferenceData)hyperlinkReferenceData.reference;\n                List<Container.Entry> entries;\n                String fragment;\n\n                switch (moduleInfoReferenceData.type) {\n                    case TYPE:\n                        entries = IndexesUtil.findInternalTypeName(collectionOfFutureIndexes, fragment = moduleInfoReferenceData.typeName);\n                        break;\n                    case PACKAGE:\n                        entries = IndexesUtil.find(collectionOfFutureIndexes, \"packageDeclarations\", moduleInfoReferenceData.typeName);\n                        fragment = null;\n                        break;\n                    default: // MODULE\n                        entries = IndexesUtil.find(collectionOfFutureIndexes, \"javaModuleDeclarations\", moduleInfoReferenceData.name);\n                        fragment = moduleInfoReferenceData.typeName;\n                        break;\n                }\n\n                if (entries.contains(entry)) {\n                    api.openURI(uri);\n                } else {\n                    String rootUri = entry.getContainer().getRoot().getUri().toString();\n                    ArrayList<Container.Entry> sameContainerEntries = new ArrayList<>();\n\n                    for (Container.Entry entry : entries) {\n                        if (entry.getUri().toString().startsWith(rootUri)) {\n                            sameContainerEntries.add(entry);\n                        }\n                    }\n\n                    if (sameContainerEntries.size() > 0) {\n                        api.openURI(x, y, sameContainerEntries, null, fragment);\n                    } else if (entries.size() > 0) {\n                        api.openURI(x, y, entries, null, fragment);\n                    }\n                }\n            } catch (URISyntaxException e) {\n                assert ExceptionUtil.printStackTrace(e);\n            }\n        }\n    }\n\n    // --- UriOpenable --- //\n    @Override\n    public boolean openUri(URI uri) {\n        ArrayList<DocumentRange> ranges = new ArrayList<>();\n        String fragment = uri.getFragment();\n        String query = uri.getQuery();\n\n        Marker.clearMarkAllHighlights(textArea);\n\n        if ((fragment != null) && (declarations.size() == 1)) {\n            DeclarationData declaration = declarations.entrySet().iterator().next().getValue();\n\n            if (fragment.equals(declaration.typeName)) {\n                ranges.add(new DocumentRange(declaration.startPosition, declaration.endPosition));\n            }\n        }\n\n        if (query != null) {\n            Map<String, String> parameters = parseQuery(query);\n\n            String highlightFlags = parameters.get(\"highlightFlags\");\n            String highlightPattern = parameters.get(\"highlightPattern\");\n\n            if ((highlightFlags != null) && (highlightPattern != null)) {\n                String regexp = createRegExp(highlightPattern);\n                Pattern pattern = Pattern.compile(regexp + \".*\");\n\n                boolean t = (highlightFlags.indexOf('t') != -1); // Highlight types\n                boolean M = (highlightFlags.indexOf('M') != -1); // Highlight modules\n\n                if (highlightFlags.indexOf('d') != -1) {\n                    // Highlight declarations\n                    for (Map.Entry<String, DeclarationData> entry : declarations.entrySet()) {\n                        DeclarationData declaration = entry.getValue();\n\n                        if (M) {\n                            matchAndAddDocumentRange(pattern, declaration.name, declaration.startPosition, declaration.endPosition, ranges);\n                        }\n                    }\n                }\n\n                if (highlightFlags.indexOf('r') != -1) {\n                    // Highlight references\n                    for (Map.Entry<Integer, HyperlinkData> entry : hyperlinks.entrySet()) {\n                        HyperlinkData hyperlink = entry.getValue();\n                        ReferenceData reference = ((HyperlinkReferenceData)hyperlink).reference;\n                        ModuleInfoReferenceData moduleInfoReferenceData = (ModuleInfoReferenceData)reference;\n\n                        if (t && (moduleInfoReferenceData.type == TYPE)) {\n                            matchAndAddDocumentRange(pattern, getMostInnerTypeName(moduleInfoReferenceData.typeName), hyperlink.startPosition, hyperlink.endPosition, ranges);\n                        }\n                        if (M && (moduleInfoReferenceData.type == MODULE)) {\n                            matchAndAddDocumentRange(pattern, moduleInfoReferenceData.name, hyperlink.startPosition, hyperlink.endPosition, ranges);\n                        }\n                    }\n                }\n            }\n        }\n\n        if ((ranges != null) && !ranges.isEmpty()) {\n            textArea.setMarkAllHighlightColor(SELECT_HIGHLIGHT_COLOR);\n            Marker.markAll(textArea, ranges);\n            ranges.sort(null);\n            setCaretPositionAndCenter(ranges.get(0));\n        }\n\n        return true;\n    }\n\n    // --- IndexesChangeListener --- //\n    @Override\n    public void indexesChanged(Collection<Future<Indexes>> collectionOfFutureIndexes) {\n        // Update the list of containers\n        this.collectionOfFutureIndexes = collectionOfFutureIndexes;\n        // Refresh links\n        boolean refresh = false;\n\n        for (ReferenceData reference : references) {\n            ModuleInfoReferenceData moduleInfoReferenceData = (ModuleInfoReferenceData)reference;\n            boolean enabled = false;\n\n            try {\n                for (Future<Indexes> futureIndexes : collectionOfFutureIndexes) {\n                    if (futureIndexes.isDone()) {\n                        Map<String, Collection> index;\n                        String key;\n\n                        switch (moduleInfoReferenceData.type) {\n                            case TYPE:\n                                index = futureIndexes.get().getIndex(\"typeDeclarations\");\n                                key = reference.typeName;\n                                break;\n                            case PACKAGE:\n                                index = futureIndexes.get().getIndex(\"packageDeclarations\");\n                                key = reference.typeName;\n                                break;\n                            default: // MODULE\n                                index = futureIndexes.get().getIndex(\"javaModuleDeclarations\");\n                                key = reference.name;\n                                break;\n                        }\n\n                        if ((index != null) && (index.get(key) != null)) {\n                            enabled = true;\n                            break;\n                        }\n                    }\n                }\n            } catch (Exception e) {\n                assert ExceptionUtil.printStackTrace(e);\n            }\n\n            if (reference.enabled != enabled) {\n                reference.enabled = enabled;\n                refresh = true;\n            }\n        }\n\n        if (refresh) {\n            textArea.repaint();\n        }\n    }\n\n    protected static class ModuleInfoReferenceData extends ReferenceData {\n        public int type;\n\n        public ModuleInfoReferenceData(int type, String typeName, String name, String descriptor, String owner) {\n            super(typeName, name, descriptor, owner);\n            this.type = type;\n        }\n    }\n\n    public class ModuleInfoFilePrinter extends StringBuilderPrinter {\n        protected HashMap<String, ReferenceData> referencesCache = new HashMap<>();\n\n        @Override\n        public void start(int maxLineNumber, int majorVersion, int minorVersion) {}\n\n        @Override\n        public void end() {\n            setText(stringBuffer.toString());\n            initLineNumbers();\n        }\n\n        @Override\n        public void printDeclaration(int type, String internalTypeName, String name, String descriptor) {\n            declarations.put(internalTypeName, new TypePage.DeclarationData(stringBuffer.length(), name.length(), internalTypeName, name, descriptor));\n            super.printDeclaration(type, internalTypeName, name, descriptor);\n        }\n\n        @Override\n        public void printReference(int type, String internalTypeName, String name, String descriptor, String ownerInternalName) {\n            String key = (type == MODULE) ? name : internalTypeName;\n            ReferenceData reference = referencesCache.get(key);\n\n            if (reference == null) {\n                reference = new ModuleInfoReferenceData(type, internalTypeName, name, descriptor, ownerInternalName);\n                referencesCache.put(key, reference);\n                references.add(reference);\n            }\n\n            addHyperlink(new HyperlinkReferenceData(stringBuffer.length(), name.length(), reference));\n            super.printReference(type, internalTypeName, name, descriptor, ownerInternalName);\n        }\n    }\n\n    // https://github.com/bobbylight/RSyntaxTextArea/wiki/Adding-Syntax-Highlighting-for-a-new-Language\n    public static class ModuleInfoTokenMaker extends AbstractTokenMaker {\n        @Override\n        public TokenMap getWordsToHighlight() {\n            TokenMap tokenMap = new TokenMap();\n\n            tokenMap.put(\"exports\", Token.RESERVED_WORD);\n            tokenMap.put(\"module\", Token.RESERVED_WORD);\n            tokenMap.put(\"open\", Token.RESERVED_WORD);\n            tokenMap.put(\"opens\", Token.RESERVED_WORD);\n            tokenMap.put(\"provides\", Token.RESERVED_WORD);\n            tokenMap.put(\"requires\", Token.RESERVED_WORD);\n            tokenMap.put(\"to\", Token.RESERVED_WORD);\n            tokenMap.put(\"transitive\", Token.RESERVED_WORD);\n            tokenMap.put(\"uses\", Token.RESERVED_WORD);\n            tokenMap.put(\"with\", Token.RESERVED_WORD);\n\n            return tokenMap;\n        }\n\n        @Override\n        public void addToken(Segment segment, int start, int end, int tokenType, int startOffset) {\n            // This assumes all keywords, etc. were parsed as \"identifiers.\"\n            if (tokenType==Token.IDENTIFIER) {\n                int value = wordsToHighlight.get(segment, start, end);\n                if (value != -1) {\n                    tokenType = value;\n                }\n            }\n            super.addToken(segment, start, end, tokenType, startOffset);\n        }\n\n        @Override\n        public Token getTokenList(Segment text, int startTokenType, int startOffset) {\n            resetTokenList();\n\n            char[] array = text.array;\n            int offset = text.offset;\n            int end = offset + text.count;\n\n            int newStartOffset = startOffset - offset;\n\n            int currentTokenStart = offset;\n            int currentTokenType  = startTokenType;\n\n            for (int i=offset; i<end; i++) {\n                char c = array[i];\n\n                switch (currentTokenType) {\n                    case Token.NULL:\n                        currentTokenStart = i;   // Starting a new token here.\n                        if (RSyntaxUtilities.isLetter(c) || (c == '_')) {\n                            currentTokenType = Token.IDENTIFIER;\n                        } else {\n                            currentTokenType = Token.WHITESPACE;\n                        }\n                        break;\n                    default: // Should never happen\n                    case Token.WHITESPACE:\n                        if (RSyntaxUtilities.isLetter(c) || (c == '_')) {\n                            addToken(text, currentTokenStart, i-1, Token.WHITESPACE, newStartOffset+currentTokenStart);\n                            currentTokenStart = i;\n                            currentTokenType = Token.IDENTIFIER;\n                        }\n                        break;\n                    case Token.IDENTIFIER:\n                        if (!RSyntaxUtilities.isLetterOrDigit(c) && (c != '_') && (c != '.')) {\n                            addToken(text, currentTokenStart, i-1, Token.IDENTIFIER, newStartOffset+currentTokenStart);\n                            currentTokenStart = i;\n                            currentTokenType = Token.WHITESPACE;\n                        }\n                        break;\n                }\n            }\n\n            if (currentTokenType == Token.NULL) {\n                addNullToken();\n            }else {\n                addToken(text, currentTokenStart,end-1, currentTokenType, newStartOffset+currentTokenStart);\n                addNullToken();\n            }\n\n            return firstToken;\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/view/component/OneTypeReferencePerLinePage.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view.component;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.IndexesChangeListener;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.util.index.IndexesUtil;\n\nimport java.awt.*;\nimport java.io.BufferedReader;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.*;\nimport java.util.List;\nimport java.util.concurrent.Future;\n\npublic class OneTypeReferencePerLinePage extends TypeReferencePage implements UriGettable, IndexesChangeListener {\n    protected API api;\n    protected Container.Entry entry;\n    protected Collection<Future<Indexes>> collectionOfFutureIndexes = Collections.emptyList();\n\n    public OneTypeReferencePerLinePage(API api, Container.Entry entry) {\n        this.api = api;\n        this.entry = entry;\n        // Load content file & Create hyperlinks\n        StringBuilder sb = new StringBuilder();\n        int offset = 0;\n\n        try (BufferedReader br = new BufferedReader(new InputStreamReader(entry.getInputStream()))) {\n            String line;\n\n            while ((line = br.readLine()) != null) {\n                String trim = line.trim();\n\n                if (trim.length() > 0) {\n                    int startIndex = offset + line.indexOf(trim);\n                    int endIndex = startIndex + trim.length();\n                    String internalTypeName = trim.replace('.', '/');\n\n                    addHyperlink(new TypeReferencePage.TypeHyperlinkData(startIndex, endIndex, internalTypeName));\n                }\n\n                offset += line.length() + 1;\n                sb.append(line).append('\\n');\n            }\n        } catch (IOException e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n\n        // Display\n        setText(sb.toString());\n    }\n\n    protected boolean isHyperlinkEnabled(HyperlinkData hyperlinkData) { return ((TypeHyperlinkData)hyperlinkData).enabled; }\n\n    protected void openHyperlink(int x, int y, HyperlinkData hyperlinkData) {\n        TypeHyperlinkData data = (TypeHyperlinkData)hyperlinkData;\n\n        if (data.enabled) {\n            try {\n                // Save current position in history\n                Point location = textArea.getLocationOnScreen();\n                int offset = textArea.viewToModel(new Point(x-location.x, y-location.y));\n                URI uri = entry.getUri();\n                api.addURI(new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), \"position=\" + offset, null));\n\n                // Open link\n                String internalTypeName = data.internalTypeName;\n                List<Container.Entry> entries = IndexesUtil.findInternalTypeName(collectionOfFutureIndexes, internalTypeName);\n                String rootUri = entry.getContainer().getRoot().getUri().toString();\n                ArrayList<Container.Entry> sameContainerEntries = new ArrayList<>();\n\n                for (Container.Entry entry : entries) {\n                    if (entry.getUri().toString().startsWith(rootUri)) {\n                        sameContainerEntries.add(entry);\n                    }\n                }\n\n                if (sameContainerEntries.size() > 0) {\n                    api.openURI(x, y, sameContainerEntries, null, data.internalTypeName);\n                } else if (entries.size() > 0) {\n                    api.openURI(x, y, entries, null, data.internalTypeName);\n                }\n            } catch (URISyntaxException e) {\n                assert ExceptionUtil.printStackTrace(e);\n            }\n        }\n    }\n\n    // --- UriGettable --- //\n    public URI getUri() { return entry.getUri(); }\n\n    // --- ContentSavable --- //\n    public String getFileName() {\n        String path = entry.getPath();\n        int index = path.lastIndexOf('/');\n        return path.substring(index+1);\n    }\n\n    // --- IndexesChangeListener --- //\n    public void indexesChanged(Collection<Future<Indexes>> collectionOfFutureIndexes) {\n        // Update the list of containers\n        this.collectionOfFutureIndexes = collectionOfFutureIndexes;\n        // Refresh links\n        boolean refresh = false;\n\n        for (Map.Entry<Integer, HyperlinkData> entry : hyperlinks.entrySet()) {\n            TypeHyperlinkData entryData = (TypeHyperlinkData)entry.getValue();\n            String internalTypeName = entryData.internalTypeName;\n            boolean enabled = IndexesUtil.containsInternalTypeName(collectionOfFutureIndexes, internalTypeName);\n\n            if (entryData.enabled != enabled) {\n                entryData.enabled = enabled;\n                refresh = true;\n            }\n        }\n\n        if (refresh) {\n            textArea.repaint();\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/view/component/RoundMarkErrorStrip.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view.component;\n\nimport org.fife.ui.rsyntaxtextarea.DocumentRange;\nimport org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;\nimport org.fife.ui.rsyntaxtextarea.parser.Parser;\nimport org.fife.ui.rsyntaxtextarea.parser.ParserNotice;\nimport org.fife.ui.rsyntaxtextarea.parser.TaskTagParser.TaskNotice;\nimport org.fife.ui.rtextarea.RTextArea;\n\nimport javax.swing.*;\nimport javax.swing.event.CaretEvent;\nimport javax.swing.event.CaretListener;\nimport javax.swing.text.BadLocationException;\nimport java.awt.*;\nimport java.awt.event.MouseAdapter;\nimport java.awt.event.MouseEvent;\nimport java.beans.PropertyChangeEvent;\nimport java.beans.PropertyChangeListener;\nimport java.text.MessageFormat;\nimport java.util.*;\nimport java.util.List;\n\n/*\n * 'private' access prohibit all changes ==> Copy \"ErrorStrip\" to JD-GUI project just to change the marker.\n *\n * JD-GUI uses two workarounds for RSyntaxTextArea:\n * - org.fife.ui.rtextarea.Marker\n * - org.jd.gui.view.component.RoundMarkErrorStrip\n */\n\n/*\n * 08/10/2009\n *\n * ErrorStrip.java - A component that can visually show Parser messages (syntax\n * errors, etc.) in an RSyntaxTextArea.\n *\n * This library is distributed under a modified BSD license.  See the included\n * RSyntaxTextArea.License.txt file for details.\n */\n\n/**\n * A component to sit alongside an {@link RSyntaxTextArea} that displays\n * colored markers for locations of interest (parser errors, marked\n * occurrences, etc.).<p>\n *\n * <code>ErrorStrip</code>s display <code>ParserNotice</code>s from\n * {@link Parser}s.  Currently, the only way to get lines flagged in this\n * component is to register a <code>Parser</code> on an RSyntaxTextArea and\n * return <code>ParserNotice</code>s for each line to display an icon for.\n * The severity of each notice must be at least the threshold set by\n * {@link #setLevelThreshold(ParserNotice.Level)}\n * to be displayed in this error strip.  The default threshold is\n * {@link ParserNotice.Level#WARNING}.<p>\n *\n * An <code>ErrorStrip</code> can be added to a UI like so:\n * <pre>\n * textArea = createTextArea();\n * textArea.addParser(new MyParser(textArea)); // Identifies lines to display\n * scrollPane = new RTextScrollPane(textArea, true);\n * ErrorStrip es = new ErrorStrip(textArea);\n * JPanel temp = new JPanel(new BorderLayout());\n * temp.add(scrollPane);\n * temp.add(es, BorderLayout.LINE_END);\n * </pre>\n *\n * @author Robert Futrell\n * @version 0.5\n */\n/*\n * Possible improvements:\n *    1. Handle marked occurrence changes & \"mark all\" changes separately from\n *       parser changes. For each property change, call a method that removes\n *       the notices being reloaded from the Markers (removing any Markers that\n *       are now \"empty\").\n */\npublic class RoundMarkErrorStrip extends JComponent {\n\n    /**\n     * The text area.\n     */\n    private RSyntaxTextArea textArea;\n\n    /**\n     * Listens for events in this component.\n     */\n    private Listener listener;\n\n    /**\n     * Whether \"marked occurrences\" in the text area should be shown in this\n     * error strip.\n     */\n    private boolean showMarkedOccurrences;\n\n    /**\n     * Whether markers for \"mark all\" highlights should be shown in this\n     * error strip.\n     */\n    private boolean showMarkAll;\n\n    /**\n     * Mapping of colors to brighter colors.  This is kept to prevent\n     * unnecessary creation of the same Colors over and over.\n     */\n    private Map<Color, Color> brighterColors;\n\n    /**\n     * Added for JD-GUI.\n     *\n     * Mapping of colors to darker colors.  This is kept to prevent\n     * unnecessary creation of the same Colors over and over.\n     */\n    private Map<Color, Color> darkerColors;\n\n    /**\n     * Only notices of this severity (or worse) will be displayed in this\n     * error strip.\n     */\n    private ParserNotice.Level levelThreshold;\n\n    /**\n     * Whether the caret marker's location should be rendered.\n     */\n    private boolean followCaret;\n\n    /**\n     * The color to use for the caret marker.\n     */\n    private Color caretMarkerColor;\n\n    /**\n     * Where we paint the caret marker.\n     */\n    private int caretLineY;\n\n    /**\n     * The last location of the caret marker.\n     */\n    private int lastLineY;\n\n    /**\n     * The preferred width of this component.\n     */\n    private static final int PREFERRED_WIDTH = 14;\n\n    private static final String MSG = \"org.fife.ui.rsyntaxtextarea.ErrorStrip\";\n    private static final ResourceBundle msg = ResourceBundle.getBundle(MSG);\n\n    /**\n     * Constructor.\n     *\n     * @param textArea The text area we are examining.\n     */\n    public RoundMarkErrorStrip(RSyntaxTextArea textArea) {\n        this.textArea = textArea;\n        listener = new Listener();\n        ToolTipManager.sharedInstance().registerComponent(this);\n        setLayout(null); // Manually layout Markers as they can overlap\n        addMouseListener(listener);\n        setShowMarkedOccurrences(true);\n        setShowMarkAll(true);\n        setLevelThreshold(ParserNotice.Level.WARNING);\n        setFollowCaret(true);\n        setCaretMarkerColor(new Color(0x96c5fe));\n    }\n\n\n    /**\n     * Overridden so we only start listening for parser notices when this\n     * component (and presumably the text area) are visible.\n     */\n    @Override\n    public void addNotify() {\n        super.addNotify();\n        textArea.addCaretListener(listener);\n        textArea.addPropertyChangeListener(\n                RSyntaxTextArea.PARSER_NOTICES_PROPERTY, listener);\n        textArea.addPropertyChangeListener(\n                RSyntaxTextArea.MARK_OCCURRENCES_PROPERTY, listener);\n        textArea.addPropertyChangeListener(\n                RSyntaxTextArea.MARKED_OCCURRENCES_CHANGED_PROPERTY, listener);\n        textArea.addPropertyChangeListener(\n                RSyntaxTextArea.MARK_ALL_OCCURRENCES_CHANGED_PROPERTY, listener);\n        refreshMarkers();\n    }\n\n\n    /**\n     * Manually manages layout since this component uses no layout manager.\n     */\n    @Override\n    public void doLayout() {\n        for (int i=0; i<getComponentCount(); i++) {\n            Marker m = (Marker)getComponent(i);\n            m.updateLocation();\n        }\n        listener.caretUpdate(null); // Force recalculation of caret line pos\n    }\n\n\n    /**\n     * Returns a \"brighter\" color.\n     *\n     * @param c The color.\n     * @return A brighter color.\n     */\n    private Color getBrighterColor(Color c) {\n        if (brighterColors==null) {\n            brighterColors = new HashMap<Color, Color>(5); // Usually small\n        }\n        Color brighter = brighterColors.get(c);\n        if (brighter==null) {\n            // Don't use c.brighter() as it doesn't work well for blue, and\n            // also doesn't return something brighter \"enough.\"\n            int r = possiblyBrighter(c.getRed());\n            int g = possiblyBrighter(c.getGreen());\n            int b = possiblyBrighter(c.getBlue());\n            brighter = new Color(r, g, b);\n            brighterColors.put(c, brighter);\n        }\n        return brighter;\n    }\n\n\n    /**\n     * Added for JD-GUI.\n     *\n     * Returns a \"brighter\" color.\n     *\n     * @param c The color.\n     * @return A brighter color.\n     */\n    private Color getDarkerColor(Color c) {\n        if (darkerColors==null) {\n            darkerColors = new HashMap<Color, Color>(5); // Usually small\n        }\n        Color darker = darkerColors.get(c);\n        if (darker==null) {\n            // Don't use c.brighter() as it doesn't work well for blue, and\n            // also doesn't return something brighter \"enough.\"\n            int r = possiblyDarker(c.getRed());\n            int g = possiblyDarker(c.getGreen());\n            int b = possiblyDarker(c.getBlue());\n            darker = new Color(r, g, b);\n            darkerColors.put(c, darker);\n        }\n        return darker;\n    }\n\n\n    /**\n     * returns the color to use when painting the caret marker.\n     *\n     * @return The caret marker color.\n     * @see #setCaretMarkerColor(Color)\n     */\n    public Color getCaretMarkerColor() {\n        return caretMarkerColor;\n    }\n\n\n    /**\n     * Returns whether the caret's position should be drawn.\n     *\n     * @return Whether the caret's position should be drawn.\n     * @see #setFollowCaret(boolean)\n     */\n    public boolean getFollowCaret() {\n        return followCaret;\n    }\n\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public Dimension getPreferredSize() {\n        int height = textArea.getPreferredScrollableViewportSize().height;\n        return new Dimension(PREFERRED_WIDTH, height);\n    }\n\n\n    /**\n     * Returns the minimum severity a parser notice must be for it to be\n     * displayed in this error strip.  This will be one of the constants\n     * defined in the <code>ParserNotice</code> class.\n     *\n     * @return The minimum severity.\n     * @see #setLevelThreshold(ParserNotice.Level)\n     */\n    public ParserNotice.Level getLevelThreshold() {\n        return levelThreshold;\n    }\n\n\n    /**\n     * Returns whether \"mark all\" highlights are shown in this error strip.\n     *\n     * @return Whether markers are shown for \"mark all\" highlights.\n     * @see #setShowMarkAll(boolean)\n     */\n    public boolean getShowMarkAll() {\n        return showMarkAll;\n    }\n\n\n    /**\n     * Returns whether marked occurrences are shown in this error strip.\n     *\n     * @return Whether marked occurrences are shown.\n     * @see #setShowMarkedOccurrences(boolean)\n     */\n    public boolean getShowMarkedOccurrences() {\n        return showMarkedOccurrences;\n    }\n\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public String getToolTipText(MouseEvent e) {\n        String text = null;\n        int line = yToLine(e.getY());\n        if (line>-1) {\n            text = msg.getString(\"Line\");\n            text = MessageFormat.format(text, Integer.valueOf(line+1));\n        }\n        return text;\n    }\n\n\n    /**\n     * Returns the y-offset in this component corresponding to a line in the\n     * text component.\n     *\n     * @param line The line.\n     * @return The y-offset.\n     * @see #yToLine(int)\n     */\n    private int lineToY(int line) {\n        int h = textArea.getVisibleRect().height;\n        float lineCount = textArea.getLineCount();\n        return (int)(((line-1)/(lineCount-1)) * h) - 2;\n    }\n\n\n    /**\n     * Overridden to (possibly) draw the caret's position.\n     *\n     * @param g The graphics context.\n     */\n    @Override\n    protected void paintComponent(Graphics g) {\n        super.paintComponent(g);\n        if (caretLineY>-1) {\n            g.setColor(getCaretMarkerColor());\n            g.fillRect(0, caretLineY, getWidth(), 2);\n        }\n    }\n\n\n    /**\n     * Returns a possibly brighter component for a color.\n     *\n     * @param i An RGB component for a color (0-255).\n     * @return A possibly brighter value for the component.\n     */\n    private static final int possiblyBrighter(int i) {\n        if (i<255) {\n            i += (int)((255-i)*0.6f);\n        }\n        return i;\n    }\n\n\n    /**\n     * Returns a possibly darker component for a color.\n     *\n     * @param i An RGB component for a color (0-255).\n     * @return A possibly brighter value for the component.\n     */\n    private static final int possiblyDarker(int i) {\n        return i -= (int)(i*0.4f);\n    }\n\n\n    /**\n     * Refreshes the markers displayed in this error strip.\n     */\n    private void refreshMarkers() {\n\n        removeAll(); // listener is removed in Marker.removeNotify()\n        Map<Integer, Marker> markerMap = new HashMap<Integer, Marker>();\n\n        List<ParserNotice> notices = textArea.getParserNotices();\n        for (ParserNotice notice : notices) {\n            if (notice.getLevel().isEqualToOrWorseThan(levelThreshold) ||\n                    (notice instanceof TaskNotice)) {\n                Integer key = Integer.valueOf(notice.getLine());\n                Marker m = markerMap.get(key);\n                if (m==null) {\n                    m = new Marker(notice);\n                    m.addMouseListener(listener);\n                    markerMap.put(key, m);\n                    add(m);\n                }\n                else {\n                    m.addNotice(notice);\n                }\n            }\n        }\n\n        if (getShowMarkedOccurrences() && textArea.getMarkOccurrences()) {\n            List<DocumentRange> occurrences = textArea.getMarkedOccurrences();\n            addMarkersForRanges(occurrences, markerMap, textArea.getMarkOccurrencesColor());\n        }\n\n        if (getShowMarkAll() /*&& textArea.getMarkAll()*/) {\n            Color markAllColor = textArea.getMarkAllHighlightColor();\n            List<DocumentRange> ranges = textArea.getMarkAllHighlightRanges();\n            addMarkersForRanges(ranges, markerMap, markAllColor);\n        }\n\n        revalidate();\n        repaint();\n\n    }\n\n\n    /**\n     * Adds markers for a list of ranges in the document.\n     *\n     * @param ranges The list of ranges in the document.\n     * @param markerMap A mapping from line number to <code>Marker</code>.\n     * @param color The color to use for the markers.\n     */\n    private void addMarkersForRanges(List<DocumentRange> ranges,\n                                     Map<Integer, Marker> markerMap, Color color) {\n        for (DocumentRange range : ranges) {\n            int line = 0;\n            try {\n                line = textArea.getLineOfOffset(range.getStartOffset());\n            } catch (BadLocationException ble) { // Never happens\n                continue;\n            }\n            ParserNotice notice = new MarkedOccurrenceNotice(range, color);\n            Integer key = Integer.valueOf(line);\n            Marker m = markerMap.get(key);\n            if (m==null) {\n                m = new Marker(notice);\n                m.addMouseListener(listener);\n                markerMap.put(key, m);\n                add(m);\n            }\n            else {\n                if (!m.containsMarkedOccurence()) {\n                    m.addNotice(notice);\n                }\n            }\n        }\n    }\n\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public void removeNotify() {\n        super.removeNotify();\n        textArea.removeCaretListener(listener);\n        textArea.removePropertyChangeListener(\n                RSyntaxTextArea.PARSER_NOTICES_PROPERTY, listener);\n        textArea.removePropertyChangeListener(\n                RSyntaxTextArea.MARK_OCCURRENCES_PROPERTY, listener);\n        textArea.removePropertyChangeListener(\n                RSyntaxTextArea.MARKED_OCCURRENCES_CHANGED_PROPERTY, listener);\n        textArea.removePropertyChangeListener(\n                RSyntaxTextArea.MARK_ALL_OCCURRENCES_CHANGED_PROPERTY, listener);\n    }\n\n\n    /**\n     * Sets the color to use when painting the caret marker.\n     *\n     * @param color The new caret marker color.\n     * @see #getCaretMarkerColor()\n     */\n    public void setCaretMarkerColor(Color color) {\n        if (color!=null) {\n            caretMarkerColor = color;\n            listener.caretUpdate(null); // Force repaint\n        }\n    }\n\n\n    /**\n     * Toggles whether the caret's current location should be drawn.\n     *\n     * @param follow Whether the caret's current location should be followed.\n     * @see #getFollowCaret()\n     */\n    public void setFollowCaret(boolean follow) {\n        if (followCaret!=follow) {\n            if (followCaret) {\n                repaint(0,caretLineY, getWidth(),2); // Erase\n            }\n            caretLineY = -1;\n            lastLineY = -1;\n            followCaret = follow;\n            listener.caretUpdate(null); // Possibly repaint\n        }\n    }\n\n\n    /**\n     * Sets the minimum severity a parser notice must be for it to be displayed\n     * in this error strip.  This should be one of the constants defined in\n     * the <code>ParserNotice</code> class.  The default value is\n     * {@link ParserNotice.Level#WARNING}.\n     *\n     * @param level The new severity threshold.\n     * @see #getLevelThreshold()\n     * @see ParserNotice\n     */\n    public void setLevelThreshold(ParserNotice.Level level) {\n        levelThreshold = level;\n        if (isDisplayable()) {\n            refreshMarkers();\n        }\n    }\n\n\n    /**\n     * Sets whether \"mark all\" highlights are shown in this error strip.\n     *\n     * @param show Whether to show markers for \"mark all\" highlights.\n     * @see #getShowMarkAll()\n     */\n    public void setShowMarkAll(boolean show) {\n        if (show!=showMarkAll) {\n            showMarkAll = show;\n            if (isDisplayable()) { // Skip this when we're first created\n                refreshMarkers();\n            }\n        }\n    }\n\n\n    /**\n     * Sets whether marked occurrences are shown in this error strip.\n     *\n     * @param show Whether to show marked occurrences.\n     * @see #getShowMarkedOccurrences()\n     */\n    public void setShowMarkedOccurrences(boolean show) {\n        if (show!=showMarkedOccurrences) {\n            showMarkedOccurrences = show;\n            if (isDisplayable()) { // Skip this when we're first created\n                refreshMarkers();\n            }\n        }\n    }\n\n\n    /**\n     * Returns the line in the text area corresponding to a y-offset in this\n     * component.\n     *\n     * @param y The y-offset.\n     * @return The line.\n     * @see #lineToY(int)\n     */\n    private final int yToLine(int y) {\n        int line = -1;\n        int h = textArea.getVisibleRect().height;\n        if (y<h) {\n            float at = y/(float)h;\n            line = Math.round((textArea.getLineCount()-1)*at);\n        }\n        return line;\n    }\n\n\n    /**\n     * Listens for events in the error strip and its markers.\n     */\n    private class Listener extends MouseAdapter\n            implements PropertyChangeListener, CaretListener {\n\n        private Rectangle visibleRect = new Rectangle();\n\n        public void caretUpdate(CaretEvent e) {\n            if (getFollowCaret()) {\n                int line = textArea.getCaretLineNumber();\n                float percent = line / (float)(textArea.getLineCount()-1);\n                textArea.computeVisibleRect(visibleRect);\n                caretLineY = (int)(visibleRect.height*percent);\n                if (caretLineY!=lastLineY) {\n                    repaint(0,lastLineY, getWidth(), 2); // Erase old position\n                    repaint(0,caretLineY, getWidth(), 2);\n                    lastLineY = caretLineY;\n                }\n            }\n        }\n\n        @Override\n        public void mouseClicked(MouseEvent e) {\n\n            Component source = (Component)e.getSource();\n            if (source instanceof Marker) {\n                ((Marker)source).mouseClicked(e);\n                return;\n            }\n\n            int line = yToLine(e.getY());\n            if (line>-1) {\n                try {\n                    int offs = textArea.getLineStartOffset(line);\n                    textArea.setCaretPosition(offs);\n                } catch (BadLocationException ble) { // Never happens\n                    UIManager.getLookAndFeel().provideErrorFeedback(textArea);\n                }\n            }\n\n        }\n\n        public void propertyChange(PropertyChangeEvent e) {\n\n            String propName = e.getPropertyName();\n\n            // If they change whether marked occurrences are visible in editor\n            if (RSyntaxTextArea.MARK_OCCURRENCES_PROPERTY.equals(propName)) {\n                if (getShowMarkedOccurrences()) {\n                    refreshMarkers();\n                }\n            }\n\n            // If parser notices changed.\n            // TODO: Don't update \"mark all/occurrences\" markers.\n            else if (RSyntaxTextArea.PARSER_NOTICES_PROPERTY.equals(propName)) {\n                refreshMarkers();\n            }\n\n            // If marked occurrences changed.\n            // TODO: Only update \"mark occurrences\" markers, not all of them.\n            else if (RSyntaxTextArea.MARKED_OCCURRENCES_CHANGED_PROPERTY.\n                    equals(propName)) {\n                if (getShowMarkedOccurrences()) {\n                    refreshMarkers();\n                }\n            }\n\n            // If \"mark all\" occurrences changed.\n            // TODO: Only update \"mark all\" markers, not all of them.\n            else if (RTextArea.MARK_ALL_OCCURRENCES_CHANGED_PROPERTY.\n                    equals(propName)) {\n                if (getShowMarkAll()) {\n                    refreshMarkers();\n                }\n            }\n\n        }\n\n    }\n\n\n    /**\n     * A notice that wraps a \"marked occurrence.\"\n     */\n    private class MarkedOccurrenceNotice implements ParserNotice {\n\n        private DocumentRange range;\n        private Color color;\n\n        public MarkedOccurrenceNotice(DocumentRange range, Color color) {\n            this.range = range;\n            this.color = color;\n        }\n\n        public int compareTo(ParserNotice other) {\n            return 0; // Value doesn't matter\n        }\n\n        public boolean containsPosition(int pos) {\n            return pos>=range.getStartOffset() && pos<range.getEndOffset();\n        }\n\n        @Override\n        public boolean equals(Object o) {\n            // FindBugs - Define equals() when defining compareTo()\n            if (!(o instanceof ParserNotice)) {\n                return false;\n            }\n            return compareTo((ParserNotice)o)==0;\n        }\n\n        public Color getColor() {\n            return color;\n        }\n\n        /**\n         * {@inheritDoc}\n         */\n        public boolean getKnowsOffsetAndLength() {\n            return true;\n        }\n\n        public int getLength() {\n            return range.getEndOffset() - range.getStartOffset();\n        }\n\n        public Level getLevel() {\n            return Level.INFO; // Won't matter\n        }\n\n        public int getLine() {\n            try {\n                return textArea.getLineOfOffset(range.getStartOffset())+1;\n            } catch (BadLocationException ble) {\n                return 0;\n            }\n        }\n\n        public String getMessage() {\n            String text = null;\n            try {\n                String word = textArea.getText(range.getStartOffset(),\n                        getLength());\n                text = msg.getString(\"OccurrenceOf\");\n                text = MessageFormat.format(text, word);\n            } catch (BadLocationException ble) {\n                UIManager.getLookAndFeel().provideErrorFeedback(textArea);\n            }\n            return text;\n        }\n\n        public int getOffset() {\n            return range.getStartOffset();\n        }\n\n        public Parser getParser() {\n            return null;\n        }\n\n        public boolean getShowInEditor() {\n            return false; // Value doesn't matter\n        }\n\n        public String getToolTipText() {\n            return null;\n        }\n\n        @Override\n        public int hashCode() { // FindBugs, since we override equals()\n            return 0; // Value doesn't matter for us.\n        }\n\n    }\n\n\n    /**\n     * A \"marker\" in this error strip, representing one or more notices.\n     */\n    private class Marker extends JComponent {\n\n        private List<ParserNotice> notices;\n\n        public Marker(ParserNotice notice) {\n            notices = new ArrayList<ParserNotice>(1); // Usually just 1\n            addNotice(notice);\n            setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));\n            setSize(getPreferredSize());\n            ToolTipManager.sharedInstance().registerComponent(this);\n        }\n\n        public void addNotice(ParserNotice notice) {\n            notices.add(notice);\n        }\n\n        public boolean containsMarkedOccurence() {\n            boolean result = false;\n            for (int i=0; i<notices.size(); i++) {\n                if (notices.get(i) instanceof MarkedOccurrenceNotice) {\n                    result = true;\n                    break;\n                }\n            }\n            return result;\n        }\n\n        public Color getColor() {\n            // Return the color for the highest-level parser.\n            Color c = null;\n            int lowestLevel = Integer.MAX_VALUE; // ERROR is 0\n            for (ParserNotice notice : notices) {\n                if (notice.getLevel().getNumericValue()<lowestLevel) {\n                    lowestLevel = notice.getLevel().getNumericValue();\n                    c = notice.getColor();\n                }\n            }\n            return c;\n        }\n\n        @Override\n        public Dimension getPreferredSize() {\n            int w = PREFERRED_WIDTH - 4; // 2-pixel empty border\n            return new Dimension(w, 5);\n        }\n\n        @Override\n        public String getToolTipText() {\n\n            String text = null;\n\n            if (notices.size()==1) {\n                text = notices.get(0).getMessage();\n            }\n            else { // > 1\n                StringBuilder sb = new StringBuilder(\"<html>\");\n                sb.append(msg.getString(\"MultipleMarkers\"));\n                sb.append(\"<br>\");\n                for (int i=0; i<notices.size(); i++) {\n                    ParserNotice pn = notices.get(i);\n                    sb.append(\"&nbsp;&nbsp;&nbsp;- \");\n                    sb.append(pn.getMessage());\n                    sb.append(\"<br>\");\n                }\n                text = sb.toString();\n            }\n\n            return text;\n\n        }\n\n        protected void mouseClicked(MouseEvent e) {\n            ParserNotice pn = notices.get(0);\n            int offs = pn.getOffset();\n            int len = pn.getLength();\n            if (offs>-1 && len>-1) { // These values are optional\n                textArea.setSelectionStart(offs);\n                textArea.setSelectionEnd(offs+len);\n            }\n            else {\n                int line = pn.getLine();\n                try {\n                    offs = textArea.getLineStartOffset(line);\n                    textArea.setCaretPosition(offs);\n                } catch (BadLocationException ble) { // Never happens\n                    UIManager.getLookAndFeel().provideErrorFeedback(textArea);\n                }\n            }\n        }\n\n        @Override\n        protected void paintComponent(Graphics g) {\n\n            // TODO: Give \"priorities\" and always pick color of a notice with\n            // highest priority (e.g. parsing errors will usually be red).\n\n            Color color = getColor();\n            if (color==null) {\n                color = Color.GRAY;\n            }\n\n            Color brighterColor = getBrighterColor(color);\n            Color darkerColor = getDarkerColor(color);\n\n            int w = getWidth();\n            int h = getHeight();\n\n            // Draw background\n            g.setColor(color);\n            g.fillRect(0,0, w,h);\n\n            // Draw border\n            w--;\n            h--;\n\n            g.setColor(darkerColor);\n            g.drawLine(w,0, w,h);\n            g.drawRect(0,h, w,h);\n\n            g.setColor(brighterColor);\n            g.drawLine(0,0, w,0);\n            g.drawRect(0,0, 0,h);\n        }\n\n        @Override\n        public void removeNotify() {\n            super.removeNotify();\n            ToolTipManager.sharedInstance().unregisterComponent(this);\n            removeMouseListener(listener);\n        }\n\n        public void updateLocation() {\n            int line = notices.get(0).getLine();\n            int y = lineToY(line);\n            setLocation(2, y);\n        }\n\n    }\n\n\n}"
  },
  {
    "path": "services/src/main/java/org/jd/gui/view/component/TextPage.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view.component;\n\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.ContentCopyable;\nimport org.jd.gui.api.feature.ContentSavable;\nimport org.jd.gui.api.feature.ContentSelectable;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.util.io.NewlineOutputStream;\n\nimport java.awt.datatransfer.StringSelection;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.io.OutputStreamWriter;\n\npublic class TextPage extends AbstractTextPage implements ContentCopyable, ContentSelectable, ContentSavable {\n\n    // --- ContentCopyable --- //\n    @Override\n    public void copy() {\n        if (textArea.getSelectionStart() == textArea.getSelectionEnd()) {\n            getToolkit().getSystemClipboard().setContents(new StringSelection(\"\"), null);\n        } else {\n            textArea.copyAsStyledText();\n        }\n    }\n\n    // --- ContentSelectable --- //\n    @Override\n    public void selectAll() {\n        textArea.selectAll();\n    }\n\n    // --- ContentSavable --- //\n    @Override\n    public String getFileName() { return \"file.txt\"; }\n\n    @Override\n    public void save(API api, OutputStream os) {\n        try (OutputStreamWriter writer = new OutputStreamWriter(new NewlineOutputStream(os), \"UTF-8\")) {\n            writer.write(textArea.getText());\n        } catch (IOException e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/view/component/TypePage.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\npackage org.jd.gui.view.component;\n\nimport org.fife.ui.rsyntaxtextarea.DocumentRange;\nimport org.fife.ui.rtextarea.Marker;\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.FocusedTypeGettable;\nimport org.jd.gui.api.feature.IndexesChangeListener;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.feature.UriOpenable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.api.model.Type;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.util.index.IndexesUtil;\nimport org.jd.gui.util.matcher.DescriptorMatcher;\n\nimport java.awt.*;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.*;\nimport java.util.List;\nimport java.util.concurrent.Future;\nimport java.util.function.BiFunction;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\npublic abstract class TypePage extends CustomLineNumbersPage implements UriGettable, UriOpenable, IndexesChangeListener, FocusedTypeGettable {\n    protected API api;\n    protected Container.Entry entry;\n    protected Collection<Future<Indexes>> collectionOfFutureIndexes = Collections.emptyList();\n\n    protected HashMap<String, DeclarationData> declarations = new HashMap<>();\n    protected TreeMap<Integer, DeclarationData> typeDeclarations = new TreeMap<>();\n    protected ArrayList<ReferenceData> references = new ArrayList<>();\n    protected ArrayList<StringData> strings = new ArrayList<>();\n\n    public TypePage(API api, Container.Entry entry) {\n        // Init attributes\n        this.api = api;\n        this.entry = entry;\n    }\n\n    @Override\n    protected boolean isHyperlinkEnabled(HyperlinkData hyperlinkData) {\n        return ((HyperlinkReferenceData)hyperlinkData).reference.enabled;\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    protected void openHyperlink(int x, int y, HyperlinkData hyperlinkData) {\n        HyperlinkReferenceData hyperlinkReferenceData = (HyperlinkReferenceData)hyperlinkData;\n\n        if (hyperlinkReferenceData.reference.enabled) {\n            try {\n                // Save current position in history\n                Point location = textArea.getLocationOnScreen();\n                int offset = textArea.viewToModel(new Point(x - location.x, y - location.y));\n                URI uri = entry.getUri();\n                api.addURI(new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), \"position=\" + offset, null));\n\n                // Open link\n                ReferenceData reference = hyperlinkReferenceData.reference;\n                String typeName = reference.typeName;\n                List<Container.Entry> entries = IndexesUtil.findInternalTypeName(collectionOfFutureIndexes, typeName);\n                String fragment = typeName;\n\n                if (reference.name != null) {\n                    fragment += '-' + reference.name;\n                }\n                if (reference.descriptor != null) {\n                    fragment += '-' + reference.descriptor;\n                }\n\n                if (entries.contains(entry)) {\n                    api.openURI(new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), fragment));\n                } else {\n                    String rootUri = entry.getContainer().getRoot().getUri().toString();\n                    ArrayList<Container.Entry> sameContainerEntries = new ArrayList<>();\n\n                    for (Container.Entry entry : entries) {\n                        if (entry.getUri().toString().startsWith(rootUri)) {\n                            sameContainerEntries.add(entry);\n                        }\n                    }\n\n                    if (sameContainerEntries.size() > 0) {\n                        api.openURI(x, y, sameContainerEntries, null, fragment);\n                    } else if (entries.size() > 0) {\n                        api.openURI(x, y, entries, null, fragment);\n                    }\n                }\n            } catch (URISyntaxException e) {\n                assert ExceptionUtil.printStackTrace(e);\n            }\n        }\n    }\n\n    // --- UriGettable --- //\n    @Override public URI getUri() { return entry.getUri(); }\n\n    // --- UriOpenable --- //\n    /**\n     * @param uri for URI format, @see jd.gui.api.feature.UriOpenable\n     */\n    @Override\n    public boolean openUri(URI uri) {\n        ArrayList<DocumentRange> ranges = new ArrayList<>();\n        String fragment = uri.getFragment();\n        String query = uri.getQuery();\n\n        Marker.clearMarkAllHighlights(textArea);\n\n        if (fragment != null) {\n            matchFragmentAndAddDocumentRange(fragment, declarations, ranges);\n        }\n\n        if (query != null) {\n            Map<String, String> parameters = parseQuery(query);\n\n            if (parameters.containsKey(\"lineNumber\")) {\n                String lineNumber = parameters.get(\"lineNumber\");\n\n                try {\n                    goToLineNumber(Integer.parseInt(lineNumber));\n                    return true;\n                } catch (NumberFormatException e) {\n                    assert ExceptionUtil.printStackTrace(e);\n                }\n            } else if (parameters.containsKey(\"position\")) {\n                String position = parameters.get(\"position\");\n\n                try {\n                    int pos = Integer.parseInt(position);\n                    if (textArea.getDocument().getLength() > pos) {\n                        ranges.add(new DocumentRange(pos, pos));\n                    }\n                } catch (NumberFormatException e) {\n                    assert ExceptionUtil.printStackTrace(e);\n                }\n            } else {\n                matchQueryAndAddDocumentRange(parameters, declarations, hyperlinks, strings, ranges);\n            }\n        }\n\n        if ((ranges != null) && !ranges.isEmpty()) {\n            textArea.setMarkAllHighlightColor(SELECT_HIGHLIGHT_COLOR);\n            Marker.markAll(textArea, ranges);\n            ranges.sort(null);\n            setCaretPositionAndCenter(ranges.get(0));\n        }\n\n        return true;\n    }\n\n    public static void matchFragmentAndAddDocumentRange(String fragment, HashMap<String, DeclarationData> declarations, List<DocumentRange> ranges) {\n        if ((fragment.indexOf('?') != -1) || (fragment.indexOf('*') != -1)) {\n            // Unknown type and/or descriptor ==> Select all and scroll to the first one\n            int lastDash = fragment.lastIndexOf('-');\n\n            if (lastDash == -1) {\n                // Search types\n                String slashAndTypeName = fragment.substring(1);\n                String typeName = fragment.substring(2);\n\n                for (Map.Entry<String, DeclarationData> entry : declarations.entrySet()) {\n                    if (entry.getKey().endsWith(slashAndTypeName) || entry.getKey().equals(typeName)) {\n                        ranges.add(new DocumentRange(entry.getValue().startPosition, entry.getValue().endPosition));\n                    }\n                }\n            } else {\n                String prefix = fragment.substring(0, lastDash+1);\n                String suffix = fragment.substring(lastDash+1);\n                BiFunction<String, String, Boolean> matchDescriptors;\n\n                if (suffix.charAt(0) == '(') {\n                    matchDescriptors = DescriptorMatcher::matchMethodDescriptors;\n                } else {\n                    matchDescriptors = DescriptorMatcher::matchFieldDescriptors;\n                }\n\n                if (fragment.charAt(0) == '*') {\n                    // Unknown type\n                    String slashAndTypeNameAndName = prefix.substring(1);\n                    String typeNameAndName = prefix.substring(2);\n\n                    for (Map.Entry<String, DeclarationData> entry : declarations.entrySet()) {\n                        String key = entry.getKey();\n                        if ((key.indexOf(slashAndTypeNameAndName) != -1) || (key.startsWith(typeNameAndName))) {\n                            int index = key.lastIndexOf('-') + 1;\n                            if (matchDescriptors.apply(suffix, key.substring(index))) {\n                                ranges.add(new DocumentRange(entry.getValue().startPosition, entry.getValue().endPosition));\n                            }\n                        }\n                    }\n                } else {\n                    // Known type\n                    for (Map.Entry<String, DeclarationData> entry : declarations.entrySet()) {\n                        String key = entry.getKey();\n                        if (key.startsWith(prefix)) {\n                            int index = key.lastIndexOf('-') + 1;\n                            if (matchDescriptors.apply(suffix, key.substring(index))) {\n                                ranges.add(new DocumentRange(entry.getValue().startPosition, entry.getValue().endPosition));\n                            }\n                        }\n                    }\n                }\n            }\n        } else {\n            // Known type and descriptor ==> Search and high light item\n            DeclarationData data = declarations.get(fragment);\n            if (data != null) {\n                ranges.add(new DocumentRange(data.startPosition, data.endPosition));\n            } else if (fragment.endsWith(\"-<clinit>-()V\")) {\n                // 'static' bloc not found ==> Select type declaration\n                String typeName = fragment.substring(0, fragment.indexOf('-'));\n                data = declarations.get(typeName);\n                ranges.add(new DocumentRange(data.startPosition, data.endPosition));\n            }\n        }\n    }\n\n    public static void matchQueryAndAddDocumentRange(\n            Map<String, String> parameters,\n            HashMap<String, DeclarationData> declarations, TreeMap<Integer, HyperlinkData> hyperlinks, ArrayList<StringData> strings,\n            List<DocumentRange> ranges) {\n\n        String highlightFlags = parameters.get(\"highlightFlags\");\n        String highlightPattern = parameters.get(\"highlightPattern\");\n\n        if ((highlightFlags != null) && (highlightPattern != null)) {\n            String highlightScope = parameters.get(\"highlightScope\");\n            String regexp = createRegExp(highlightPattern);\n            Pattern pattern = Pattern.compile(regexp + \".*\");\n\n            if (highlightFlags.indexOf('s') != -1) {\n                // Highlight strings\n                Pattern patternForString = Pattern.compile(regexp);\n\n                for (StringData data : strings) {\n                    if (matchScope(highlightScope, data.owner)) {\n                        Matcher matcher = patternForString.matcher(data.text);\n                        int offset = data.startPosition;\n\n                        while(matcher.find()) {\n                            ranges.add(new DocumentRange(offset + matcher.start(), offset + matcher.end()));\n                        }\n                    }\n                }\n            }\n\n            boolean t = (highlightFlags.indexOf('t') != -1); // Highlight types\n            boolean f = (highlightFlags.indexOf('f') != -1); // Highlight fields\n            boolean m = (highlightFlags.indexOf('m') != -1); // Highlight methods\n            boolean c = (highlightFlags.indexOf('c') != -1); // Highlight constructors\n\n            if (highlightFlags.indexOf('d') != -1) {\n                // Highlight declarations\n                for (Map.Entry<String, DeclarationData> entry : declarations.entrySet()) {\n                    DeclarationData declaration = entry.getValue();\n\n                    if (matchScope(highlightScope, declaration.typeName)) {\n                        if ((t && declaration.isAType()) || (c && declaration.isAConstructor())) {\n                            matchAndAddDocumentRange(pattern, getMostInnerTypeName(declaration.typeName), declaration.startPosition, declaration.endPosition, ranges);\n                        }\n                        if ((f && declaration.isAField()) || (m && declaration.isAMethod())) {\n                            matchAndAddDocumentRange(pattern, declaration.name, declaration.startPosition, declaration.endPosition, ranges);\n                        }\n                    }\n                }\n            }\n\n            if (highlightFlags.indexOf('r') != -1) {\n                // Highlight references\n                for (Map.Entry<Integer, HyperlinkData> entry : hyperlinks.entrySet()) {\n                    HyperlinkData hyperlink = entry.getValue();\n                    ReferenceData reference = ((HyperlinkReferenceData)hyperlink).reference;\n\n                    if (matchScope(highlightScope, reference.owner)) {\n                        if ((t && reference.isAType()) || (c && reference.isAConstructor())) {\n                            matchAndAddDocumentRange(pattern, getMostInnerTypeName(reference.typeName), hyperlink.startPosition, hyperlink.endPosition, ranges);\n                        }\n                        if ((f && reference.isAField()) || (m && reference.isAMethod())) {\n                            matchAndAddDocumentRange(pattern, reference.name, hyperlink.startPosition, hyperlink.endPosition, ranges);\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    public static boolean matchScope(String scope, String type) {\n        if ((scope == null) || scope.isEmpty())\n            return true;\n        if (scope.charAt(0) == '*')\n            return type.endsWith(scope.substring(1)) || type.equals(scope.substring(2));\n        return type.equals(scope);\n    }\n\n    public static void matchAndAddDocumentRange(Pattern pattern, String text, int start, int end, List<DocumentRange> ranges) {\n        if (pattern.matcher(text).matches()) {\n            ranges.add(new DocumentRange(start, end));\n        }\n    }\n\n    public static String getMostInnerTypeName(String typeName) {\n        int lastPackageSeparatorIndex = typeName.lastIndexOf('/') + 1;\n        int lastTypeNameSeparatorIndex = typeName.lastIndexOf('$') + 1;\n        int lastIndex = Math.max(lastPackageSeparatorIndex, lastTypeNameSeparatorIndex);\n        return typeName.substring(lastIndex);\n    }\n\n    // --- FocusedTypeGettable --- //\n    @Override public String getFocusedTypeName() {\n        Map.Entry<Integer, DeclarationData> entry = typeDeclarations.floorEntry(textArea.getCaretPosition());\n\n        if (entry != null) {\n            DeclarationData data = entry.getValue();\n            if (data != null) {\n                return data.typeName;\n            }\n        }\n\n        return null;\n    }\n\n    @Override public Container.Entry getEntry() { return entry; }\n\n    // --- IndexesChangeListener --- //\n    @Override\n    public void indexesChanged(Collection<Future<Indexes>> collectionOfFutureIndexes) {\n        // Update the list of containers\n        this.collectionOfFutureIndexes = collectionOfFutureIndexes;\n        // Refresh links\n        boolean refresh = false;\n\n        for (ReferenceData reference : references) {\n            String typeName = reference.typeName;\n            boolean enabled;\n\n            if (reference.name == null) {\n                enabled = false;\n\n                try {\n                    for (Future<Indexes> futureIndexes : collectionOfFutureIndexes) {\n                        if (futureIndexes.isDone()) {\n                            Map<String, Collection> index = futureIndexes.get().getIndex(\"typeDeclarations\");\n                            if ((index != null) && (index.get(typeName) != null)) {\n                                enabled = true;\n                                break;\n                            }\n                        }\n                    }\n                } catch (Exception e) {\n                    assert ExceptionUtil.printStackTrace(e);\n                }\n            } else {\n                try {\n                    // Recursive search\n                    typeName = searchTypeHavingMember(typeName, reference.name, reference.descriptor, entry);\n                    if (typeName != null) {\n                        // Replace type with the real type having the referenced member\n                        reference.typeName = typeName;\n                        enabled = true;\n                    } else {\n                        enabled = false;\n                    }\n                } catch (Error e) {\n                    // Catch StackOverflowError or OutOfMemoryError\n                    assert ExceptionUtil.printStackTrace(e);\n                    enabled = false;\n                }\n            }\n\n            if (reference.enabled != enabled) {\n                reference.enabled = enabled;\n                refresh = true;\n            }\n        }\n\n        if (refresh) {\n            textArea.repaint();\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected String searchTypeHavingMember(String typeName, String name, String descriptor, Container.Entry entry) {\n        ArrayList<Container.Entry> entries = new ArrayList<>();\n\n        try {\n            for (Future<Indexes> futureIndexes : collectionOfFutureIndexes) {\n                if (futureIndexes.isDone()) {\n                    Map<String, Collection> index = futureIndexes.get().getIndex(\"typeDeclarations\");\n                    if (index != null) {\n                        Collection<Container.Entry> collection = index.get(typeName);\n                        if (collection != null) {\n                            entries.addAll(collection);\n                        }\n                    }\n                }\n            }\n        } catch (Exception e) {\n            assert ExceptionUtil.printStackTrace(e);\n        }\n\n        String rootUri = entry.getContainer().getRoot().getUri().toString();\n        ArrayList<Container.Entry> sameContainerEntries = new ArrayList<>();\n\n        for (Container.Entry e : entries) {\n            if (e.getUri().toString().startsWith(rootUri)) {\n                sameContainerEntries.add(e);\n            }\n        }\n\n        if (sameContainerEntries.size() > 0) {\n            return searchTypeHavingMember(typeName, name, descriptor, sameContainerEntries);\n        } else {\n            return searchTypeHavingMember(typeName, name, descriptor, entries);\n        }\n    }\n\n    protected String searchTypeHavingMember(String typeName, String name, String descriptor, List<Container.Entry> entries) {\n        for (Container.Entry entry : entries) {\n            Type type = api.getTypeFactory(entry).make(api, entry, typeName);\n\n            if (type != null) {\n                if (descriptor.indexOf('(') == -1) {\n                    // Search a field\n                    for (Type.Field field : type.getFields()) {\n                        if (field.getName().equals(name) && DescriptorMatcher.matchFieldDescriptors(field.getDescriptor(), descriptor)) {\n                            // Field found\n                            return typeName;\n                        }\n                    }\n                } else {\n                    // Search a method\n                    for (Type.Method method : type.getMethods()) {\n                        if (method.getName().equals(name) && DescriptorMatcher.matchMethodDescriptors(method.getDescriptor(), descriptor)) {\n                            // Method found\n                            return typeName;\n                        }\n                    }\n                }\n\n                // Not found -> Search in super type\n                String typeOwnerName = searchTypeHavingMember(type.getSuperName(), name, descriptor, entry);\n                if (typeOwnerName != null) {\n                    return typeOwnerName;\n                }\n            }\n        }\n\n        return null;\n    }\n\n    public static class StringData {\n        int startPosition;\n        int endPosition;\n        String text;\n        String owner;\n\n        public StringData(int startPosition, int length, String text, String owner) {\n            this.startPosition = startPosition;\n            this.endPosition = startPosition + length;\n            this.text = text;\n            this.owner = owner;\n        }\n    }\n\n    public static class DeclarationData {\n        int startPosition;\n        int endPosition;\n        String typeName;\n        /**\n         * Field or method name or null for type\n         */\n        String name;\n        String descriptor;\n\n        public DeclarationData(int startPosition, int length, String typeName, String name, String descriptor) {\n            this.startPosition = startPosition;\n            this.endPosition = startPosition + length;\n            this.typeName = typeName;\n            this.name = name;\n            this.descriptor = descriptor;\n        }\n\n        public boolean isAType() { return name == null; }\n        public boolean isAField() { return (descriptor != null) && descriptor.charAt(0) != '('; }\n        public boolean isAMethod() { return (descriptor != null) && descriptor.charAt(0) == '('; }\n        public boolean isAConstructor() { return \"<init>\".equals(name); }\n    }\n\n    public static class HyperlinkReferenceData extends HyperlinkData {\n        public ReferenceData reference;\n\n        public HyperlinkReferenceData(int startPosition, int length, ReferenceData reference) {\n            super(startPosition, startPosition+length);\n            this.reference = reference;\n        }\n    }\n\n    protected static class ReferenceData {\n        public String typeName;\n        /**\n         * Field or method name or null for type\n         */\n        public String name;\n        /**\n         * Field or method descriptor or null for type\n         */\n        public String descriptor;\n        /**\n         * Internal type name containing reference or null for \"import\" statement.\n         * Used to high light items matching with URI like \"file://dir1/dir2/file?highlightPattern=hello&highlightFlags=drtcmfs&highlightScope=type\".\n         */\n        public String owner;\n        /**\n         * \"Enabled\" flag for link of reference\n         */\n        public boolean enabled = false;\n\n        public ReferenceData(String typeName, String name, String descriptor, String owner) {\n            this.typeName = typeName;\n            this.name = name;\n            this.descriptor = descriptor;\n            this.owner = owner;\n        }\n\n        boolean isAType() { return name == null; }\n        boolean isAField() { return (descriptor != null) && descriptor.charAt(0) != '('; }\n        boolean isAMethod() { return (descriptor != null) && descriptor.charAt(0) == '('; }\n        boolean isAConstructor() { return \"<init>\".equals(name); }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/view/component/TypeReferencePage.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view.component;\n\nimport org.fife.ui.rsyntaxtextarea.DocumentRange;\nimport org.fife.ui.rtextarea.Marker;\nimport org.jd.gui.util.exception.ExceptionUtil;\n\nimport java.net.URI;\nimport java.util.ArrayList;\nimport java.util.Map;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * Page containing type references (Hyperlinks to pages of type)\n */\npublic abstract class TypeReferencePage extends HyperlinkPage {\n\n    // --- UriOpenable --- //\n    public boolean openUri(URI uri) {\n        ArrayList<DocumentRange> ranges = new ArrayList<>();\n        String query = uri.getQuery();\n\n        Marker.clearMarkAllHighlights(textArea);\n\n        if (query != null) {\n            Map<String, String> parameters = parseQuery(query);\n\n            if (parameters.containsKey(\"lineNumber\")) {\n                String lineNumber = parameters.get(\"lineNumber\");\n\n                try {\n                    goToLineNumber(Integer.parseInt(lineNumber));\n                    return true;\n                } catch (NumberFormatException e) {\n                    assert ExceptionUtil.printStackTrace(e);\n                }\n            } else if (parameters.containsKey(\"position\")) {\n                String position = parameters.get(\"position\");\n\n                try {\n                    int pos = Integer.parseInt(position);\n                    if (textArea.getDocument().getLength() > pos) {\n                        ranges.add(new DocumentRange(pos, pos));\n                    }\n                } catch (NumberFormatException e) {\n                    assert ExceptionUtil.printStackTrace(e);\n                }\n            } else {\n                String highlightFlags = parameters.get(\"highlightFlags\");\n                String highlightPattern = parameters.get(\"highlightPattern\");\n\n                if ((highlightFlags != null) && (highlightPattern != null)) {\n                    String regexp = createRegExp(highlightPattern);\n\n                    if (highlightFlags.indexOf('s') != -1) {\n                        // Highlight strings\n                        Pattern pattern = Pattern.compile(regexp);\n                        Matcher matcher = pattern.matcher(textArea.getText());\n\n                        while (matcher.find()) {\n                            ranges.add(new DocumentRange(matcher.start(), matcher.end()));\n                        }\n                    }\n\n                    if ((highlightFlags.indexOf('t') != -1) && (highlightFlags.indexOf('r') != -1)) {\n                        // Highlight type references\n                        Pattern pattern = Pattern.compile(regexp + \".*\");\n\n                        for (Map.Entry<Integer, HyperlinkData> entry : hyperlinks.entrySet()) {\n                            TypeHyperlinkData hyperlink = (TypeHyperlinkData)entry.getValue();\n                            String name = getMostInnerTypeName(hyperlink.internalTypeName);\n\n                            if (pattern.matcher(name).matches()) {\n                                ranges.add(new DocumentRange(hyperlink.startPosition, hyperlink.endPosition));\n                            }\n                        }\n                    }\n                }\n            }\n        }\n\n        if ((ranges != null) && !ranges.isEmpty()) {\n            textArea.setMarkAllHighlightColor(SELECT_HIGHLIGHT_COLOR);\n            Marker.markAll(textArea, ranges);\n            ranges.sort(null);\n            setCaretPositionAndCenter(ranges.get(0));\n        }\n\n        return false;\n    }\n\n    public String getMostInnerTypeName(String typeName) {\n        int lastPackageSeparatorIndex = typeName.lastIndexOf('/') + 1;\n        int lastTypeNameSeparatorIndex = typeName.lastIndexOf('$') + 1;\n        int lastIndex = Math.max(lastPackageSeparatorIndex, lastTypeNameSeparatorIndex);\n        return typeName.substring(lastIndex);\n    }\n\n    public static class TypeHyperlinkData extends HyperlinkData {\n        public boolean enabled;\n        public String internalTypeName;\n\n        TypeHyperlinkData(int startPosition, int endPosition, String internalTypeName) {\n            super(startPosition, endPosition);\n            this.enabled = false;\n            this.internalTypeName = internalTypeName;\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/view/component/WebXmlFilePage.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view.component;\n\nimport org.fife.ui.rsyntaxtextarea.SyntaxConstants;\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.IndexesChangeListener;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.util.index.IndexesUtil;\nimport org.jd.gui.util.io.TextReader;\nimport org.jd.gui.util.xml.AbstractXmlPathFinder;\n\nimport java.awt.*;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.*;\nimport java.util.List;\nimport java.util.concurrent.Future;\n\npublic class WebXmlFilePage extends TypeReferencePage implements UriGettable, IndexesChangeListener {\n    protected API api;\n    protected Container.Entry entry;\n    protected Collection<Future<Indexes>> collectionOfFutureIndexes;\n\n    public WebXmlFilePage(API api, Container.Entry entry) {\n        this.api = api;\n        this.entry = entry;\n        // Load content file\n        String text = TextReader.getText(entry.getInputStream());\n        // Create hyperlinks\n        new PathFinder().find(text);\n        // Display\n        setText(text);\n    }\n\n    public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_XML; }\n\n    protected boolean isHyperlinkEnabled(HyperlinkData hyperlinkData) { return ((TypeHyperlinkData)hyperlinkData).enabled; }\n\n    protected void openHyperlink(int x, int y, HyperlinkData hyperlinkData) {\n        TypeHyperlinkData data = (TypeHyperlinkData)hyperlinkData;\n\n        if (data.enabled) {\n            try {\n                // Save current position in history\n                Point location = textArea.getLocationOnScreen();\n                int offset = textArea.viewToModel(new Point(x-location.x, y-location.y));\n                URI uri = entry.getUri();\n                api.addURI(new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), \"position=\" + offset, null));\n\n                // Open link\n                if (hyperlinkData instanceof PathHyperlinkData) {\n                    PathHyperlinkData d = (PathHyperlinkData)hyperlinkData;\n                    String path = d.path;\n                    Container.Entry entry = searchEntry(this.entry.getContainer().getRoot(), path);\n                    if (entry != null) {\n                        api.openURI(x, y, Collections.singletonList(entry), null, path);\n                    }\n                } else {\n                    String internalTypeName = data.internalTypeName;\n                    List<Container.Entry> entries = IndexesUtil.findInternalTypeName(collectionOfFutureIndexes, internalTypeName);\n                    String rootUri = entry.getContainer().getRoot().getUri().toString();\n                    ArrayList<Container.Entry> sameContainerEntries = new ArrayList<>();\n\n                    for (Container.Entry entry : entries) {\n                        if (entry.getUri().toString().startsWith(rootUri)) {\n                            sameContainerEntries.add(entry);\n                        }\n                    }\n\n                    if (sameContainerEntries.size() > 0) {\n                        api.openURI(x, y, sameContainerEntries, null, data.internalTypeName);\n                    } else if (entries.size() > 0) {\n                        api.openURI(x, y, entries, null, data.internalTypeName);\n                    }\n                }\n            } catch (URISyntaxException e) {\n                assert ExceptionUtil.printStackTrace(e);\n            }\n        }\n    }\n\n    public static Container.Entry searchEntry(Container.Entry parent, String path) {\n        if (path.charAt(0) == '/')\n            path = path.substring(1);\n        return recursiveSearchEntry(parent, path);\n    }\n\n    public static Container.Entry recursiveSearchEntry(Container.Entry parent, String path) {\n        Container.Entry entry = null;\n\n        for (Container.Entry child : parent.getChildren()) {\n            if (path.equals(child.getPath())) {\n                entry = child;\n                break;\n            }\n        }\n\n        if (entry != null) {\n            return entry;\n        } else {\n            for (Container.Entry child : parent.getChildren()) {\n                if (path.startsWith(child.getPath() + '/')) {\n                    entry = child;\n                    break;\n                }\n            }\n\n            return (entry != null) ? searchEntry(entry, path) : null;\n        }\n    }\n\n    // --- UriGettable --- //\n    public URI getUri() { return entry.getUri(); }\n\n    // --- ContentSavable --- //\n    public String getFileName() {\n        String path = entry.getPath();\n        int index = path.lastIndexOf('/');\n        return path.substring(index+1);\n    }\n\n    // --- IndexesChangeListener --- //\n    public void indexesChanged(Collection<Future<Indexes>> collectionOfFutureIndexes) {\n        // Update the list of containers\n        this.collectionOfFutureIndexes = collectionOfFutureIndexes;\n        // Refresh links\n        boolean refresh = false;\n\n        for (Map.Entry<Integer, HyperlinkData> entry : hyperlinks.entrySet()) {\n            TypeHyperlinkData data = (TypeHyperlinkData)entry.getValue();\n            boolean enabled;\n\n            if (data instanceof PathHyperlinkData) {\n                PathHyperlinkData d = (PathHyperlinkData)data;\n                enabled = searchEntry(this.entry.getContainer().getRoot(), d.path) != null;\n            } else {\n                String internalTypeName = data.internalTypeName;\n                enabled = IndexesUtil.containsInternalTypeName(collectionOfFutureIndexes, internalTypeName);\n            }\n\n            if (data.enabled != enabled) {\n                data.enabled = enabled;\n                refresh = true;\n            }\n        }\n\n        if (refresh) {\n            textArea.repaint();\n        }\n    }\n\n    public static class PathHyperlinkData extends TypeHyperlinkData {\n        public boolean enabled;\n        public String path;\n\n        PathHyperlinkData(int startPosition, int endPosition, String path) {\n            super(startPosition, endPosition, null);\n            this.enabled = false;\n            this.path = path;\n        }\n    }\n\n    protected static List<String> typeHyperlinkPaths = Arrays.asList(\n        \"web-app/filter/filter-class\",\n        \"web-app/listener/listener-class\",\n        \"web-app/servlet/servlet-class\");\n\n    protected static List<String> pathHyperlinkPaths = Arrays.asList(\n        \"web-app/jsp-config/taglib/taglib-location\",\n        \"web-app/welcome-file-list/welcome-file\",\n        \"web-app/login-config/form-login-config/form-login-page\",\n        \"web-app/login-config/form-login-config/form-error-page\",\n        \"web-app/jsp-config/jsp-property-group/include-prelude\",\n        \"web-app/jsp-config/jsp-property-group/include-coda\");\n\n    protected static List<String> hyperlinkPaths = new ArrayList<>(typeHyperlinkPaths.size() + pathHyperlinkPaths.size());\n\n    static {\n        hyperlinkPaths.addAll(typeHyperlinkPaths);\n        hyperlinkPaths.addAll(pathHyperlinkPaths);\n    }\n\n    public class PathFinder extends AbstractXmlPathFinder {\n        public PathFinder() {\n            super(hyperlinkPaths);\n        }\n\n        public void handle(String path, String text, int position) {\n            String trim = text.trim();\n            if (trim != null) {\n                int startIndex = position + text.indexOf(trim);\n                int endIndex = startIndex + trim.length();\n\n                if (pathHyperlinkPaths.contains(path)) {\n                    addHyperlink(new PathHyperlinkData(startIndex, endIndex, trim));\n                } else {\n                    String internalTypeName = trim.replace('.', '/');\n                    addHyperlink(new TypeHyperlinkData(startIndex, endIndex, internalTypeName));\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/view/component/XmlFilePage.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view.component;\n\nimport org.fife.ui.rsyntaxtextarea.SyntaxConstants;\nimport org.jd.gui.api.API;\nimport org.jd.gui.api.feature.IndexesChangeListener;\nimport org.jd.gui.api.feature.UriGettable;\nimport org.jd.gui.api.model.Container;\nimport org.jd.gui.api.model.Indexes;\nimport org.jd.gui.util.exception.ExceptionUtil;\nimport org.jd.gui.util.index.IndexesUtil;\nimport org.jd.gui.util.io.TextReader;\n\nimport java.awt.*;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.Future;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\npublic class XmlFilePage extends TypeReferencePage implements UriGettable, IndexesChangeListener {\n    protected API api;\n    protected Container.Entry entry;\n    protected Collection<Future<Indexes>> collectionOfFutureIndexes;\n\n    public XmlFilePage(API api, Container.Entry entry) {\n        this.api = api;\n        this.entry = entry;\n        // Load content file\n        String text = TextReader.getText(entry.getInputStream());\n        // Create hyperlinks\n        Pattern pattern = Pattern.compile(\"(?s)<\\\\s*bean[^<]+class\\\\s*=\\\\s*\\\"([^\\\"]*)\\\"\");\n        Matcher matcher = pattern.matcher(textArea.getText());\n\n        while (matcher.find()) {\n            // Spring type reference found\n            String value = matcher.group(1);\n            String trim = value.trim();\n\n            if (trim != null) {\n                int startIndex = matcher.start(1) - 1;\n                int endIndex = startIndex + value.length() + 2;\n                String internalTypeName = trim.replace('.', '/');\n                addHyperlink(new TypeHyperlinkData(startIndex, endIndex, internalTypeName));\n            }\n        }\n        // Display\n        setText(text);\n    }\n\n    public String getSyntaxStyle() { return SyntaxConstants.SYNTAX_STYLE_XML; }\n\n    protected boolean isHyperlinkEnabled(HyperlinkData hyperlinkData) { return ((TypeHyperlinkData)hyperlinkData).enabled; }\n\n    protected void openHyperlink(int x, int y, HyperlinkData hyperlinkData) {\n        TypeHyperlinkData data = (TypeHyperlinkData)hyperlinkData;\n\n        if (data.enabled) {\n            try {\n                // Save current position in history\n                Point location = textArea.getLocationOnScreen();\n                int offset = textArea.viewToModel(new Point(x-location.x, y-location.y));\n                URI uri = entry.getUri();\n                api.addURI(new URI(uri.getScheme(), uri.getAuthority(), uri.getPath(), \"position=\" + offset, null));\n\n                // Open link\n                String internalTypeName = data.internalTypeName;\n                List<Container.Entry> entries = IndexesUtil.findInternalTypeName(collectionOfFutureIndexes, internalTypeName);\n                String rootUri = entry.getContainer().getRoot().getUri().toString();\n                ArrayList<Container.Entry> sameContainerEntries = new ArrayList<>();\n\n                for (Container.Entry entry : entries) {\n                    if (entry.getUri().toString().startsWith(rootUri)) {\n                        sameContainerEntries.add(entry);\n                    }\n                }\n\n                if (sameContainerEntries.size() > 0) {\n                    api.openURI(x, y, sameContainerEntries, null, data.internalTypeName);\n                } else if (entries.size() > 0) {\n                    api.openURI(x, y, entries, null, data.internalTypeName);\n                }\n            } catch (URISyntaxException e) {\n                assert ExceptionUtil.printStackTrace(e);\n            }\n        }\n    }\n\n    // --- UriGettable --- //\n    public URI getUri() { return entry.getUri(); }\n\n    // --- ContentSavable --- //\n    public String getFileName() {\n        String path = entry.getPath();\n        int index = path.lastIndexOf('/');\n        return path.substring(index+1);\n    }\n\n    // --- IndexesChangeListener --- //\n    public void indexesChanged(Collection<Future<Indexes>> collectionOfFutureIndexes) {\n        // Update the list of containers\n        this.collectionOfFutureIndexes = collectionOfFutureIndexes;\n        // Refresh links\n        boolean refresh = false;\n\n        for (Map.Entry<Integer, HyperlinkData> entry : hyperlinks.entrySet()) {\n            TypeHyperlinkData data = (TypeHyperlinkData)entry.getValue();\n            String internalTypeName = data.internalTypeName;\n            boolean enabled = IndexesUtil.containsInternalTypeName(collectionOfFutureIndexes, internalTypeName);\n\n            if (data.enabled != enabled) {\n                data.enabled = enabled;\n                refresh = true;\n            }\n        }\n\n        if (refresh) {\n            textArea.repaint();\n        }\n    }\n}\n"
  },
  {
    "path": "services/src/main/java/org/jd/gui/view/data/TreeNodeBean.java",
    "content": "/*\n * Copyright (c) 2008-2019 Emmanuel Dupuy.\n * This project is distributed under the GPLv3 license.\n * This is a Copyleft license that gives the user the right to use,\n * copy and modify the code freely for non-commercial purposes.\n */\n\npackage org.jd.gui.view.data;\n\nimport org.jd.gui.api.model.TreeNodeData;\n\nimport javax.swing.*;\n\npublic class TreeNodeBean implements TreeNodeData {\n    protected String label;\n    protected String tip;\n    protected Icon icon;\n    protected Icon openIcon;\n\n    public TreeNodeBean(String label, Icon icon) {\n        this.label = label;\n        this.icon = icon;\n    }\n\n    public TreeNodeBean(String label, String tip, Icon icon) {\n        this.label = label;\n        this.tip = tip;\n        this.icon = icon;\n    }\n\n    public TreeNodeBean(String label, Icon icon, Icon openIcon) {\n        this.label = label;\n        this.icon = icon;\n        this.openIcon = openIcon;\n    }\n\n    public TreeNodeBean(String label, String tip, Icon icon, Icon openIcon) {\n        this.label = label;\n        this.tip = tip;\n        this.icon = icon;\n        this.openIcon = openIcon;\n    }\n\n    public void setLabel(String label) {\n        this.label = label;\n    }\n\n    public void setTip(String tip) {\n        this.tip = tip;\n    }\n\n    public void setIcon(Icon icon) {\n        this.icon = icon;\n    }\n\n    public void setOpenIcon(Icon openIcon) {\n        this.openIcon = openIcon;\n    }\n\n    @Override\n    public String getLabel() {\n\n        return label;\n    }\n\n    @Override\n    public String getTip() {\n        return tip;\n    }\n\n    @Override\n    public Icon getIcon() {\n        return icon;\n    }\n\n    @Override\n    public Icon getOpenIcon() {\n        return openIcon;\n    }\n}\n"
  },
  {
    "path": "services/src/main/resources/META-INF/services/org.jd.gui.spi.ContainerFactory",
    "content": "# Order is important : 'GenericContainerFactoryProvider' must be the last\norg.jd.gui.service.container.KarContainerFactoryProvider\norg.jd.gui.service.container.JavaModuleContainerFactoryProvider\norg.jd.gui.service.container.EarContainerFactoryProvider\norg.jd.gui.service.container.WarContainerFactoryProvider\norg.jd.gui.service.container.JarContainerFactoryProvider\norg.jd.gui.service.container.GenericContainerFactoryProvider\n"
  },
  {
    "path": "services/src/main/resources/META-INF/services/org.jd.gui.spi.ContextualActionsFactory",
    "content": "org.jd.gui.service.actions.CopyQualifiedNameContextualActionsFactory\n"
  },
  {
    "path": "services/src/main/resources/META-INF/services/org.jd.gui.spi.FileLoader",
    "content": "org.jd.gui.service.fileloader.AarFileLoaderProvider\norg.jd.gui.service.fileloader.ClassFileLoaderProvider\norg.jd.gui.service.fileloader.EarFileLoaderProvider\norg.jd.gui.service.fileloader.JarFileLoaderProvider\norg.jd.gui.service.fileloader.JavaFileLoaderProvider\norg.jd.gui.service.fileloader.JavaModuleFileLoaderProvider\norg.jd.gui.service.fileloader.KarFileLoaderProvider\norg.jd.gui.service.fileloader.LogFileLoaderProvider\norg.jd.gui.service.fileloader.WarFileLoaderProvider\norg.jd.gui.service.fileloader.ZipFileLoaderProvider\n"
  },
  {
    "path": "services/src/main/resources/META-INF/services/org.jd.gui.spi.Indexer",
    "content": "org.jd.gui.service.indexer.DirectoryIndexerProvider\norg.jd.gui.service.indexer.ClassFileIndexerProvider\norg.jd.gui.service.indexer.EjbJarXmlFileIndexerProvider\norg.jd.gui.service.indexer.JavaFileIndexerProvider\norg.jd.gui.service.indexer.JavaModuleFileIndexerProvider\norg.jd.gui.service.indexer.JavaModuleInfoFileIndexerProvider\norg.jd.gui.service.indexer.MetainfServiceFileIndexerProvider\norg.jd.gui.service.indexer.TextFileIndexerProvider\norg.jd.gui.service.indexer.WebXmlFileIndexerProvider\norg.jd.gui.service.indexer.ZipFileIndexerProvider\norg.jd.gui.service.indexer.XmlBasedFileIndexerProvider\norg.jd.gui.service.indexer.XmlFileIndexerProvider\n"
  },
  {
    "path": "services/src/main/resources/META-INF/services/org.jd.gui.spi.PasteHandler",
    "content": "org.jd.gui.service.pastehandler.LogPasteHandler\n"
  },
  {
    "path": "services/src/main/resources/META-INF/services/org.jd.gui.spi.PreferencesPanel",
    "content": "org.jd.gui.service.preferencespanel.DirectoryIndexerPreferencesProvider\norg.jd.gui.service.preferencespanel.ClassFileSaverPreferencesProvider\norg.jd.gui.service.preferencespanel.ClassFileDecompilerPreferencesProvider\norg.jd.gui.service.preferencespanel.ViewerPreferencesProvider\norg.jd.gui.service.preferencespanel.MavenOrgSourceLoaderPreferencesProvider\n"
  },
  {
    "path": "services/src/main/resources/META-INF/services/org.jd.gui.spi.SourceLoader",
    "content": "org.jd.gui.service.sourceloader.MavenOrgSourceLoaderProvider\n"
  },
  {
    "path": "services/src/main/resources/META-INF/services/org.jd.gui.spi.SourceSaver",
    "content": "org.jd.gui.service.sourcesaver.ClassFileSourceSaverProvider\norg.jd.gui.service.sourcesaver.DirectorySourceSaverProvider\norg.jd.gui.service.sourcesaver.FileSourceSaverProvider\norg.jd.gui.service.sourcesaver.PackageSourceSaverProvider\norg.jd.gui.service.sourcesaver.ZipFileSourceSaverProvider\n"
  },
  {
    "path": "services/src/main/resources/META-INF/services/org.jd.gui.spi.TreeNodeFactory",
    "content": "org.jd.gui.service.treenode.ClassesDirectoryTreeNodeFactoryProvider\norg.jd.gui.service.treenode.ClassFileTreeNodeFactoryProvider\norg.jd.gui.service.treenode.CssFileTreeNodeFactoryProvider\norg.jd.gui.service.treenode.DirectoryTreeNodeFactoryProvider\norg.jd.gui.service.treenode.DtdFileTreeNodeFactoryProvider\norg.jd.gui.service.treenode.EarFileTreeNodeFactoryProvider\norg.jd.gui.service.treenode.EjbJarXmlFileTreeNodeFactoryProvider\norg.jd.gui.service.treenode.FileTreeNodeFactoryProvider\norg.jd.gui.service.treenode.HtmlFileTreeNodeFactoryProvider\norg.jd.gui.service.treenode.JarFileTreeNodeFactoryProvider\norg.jd.gui.service.treenode.JavaFileTreeNodeFactoryProvider\norg.jd.gui.service.treenode.JavascriptFileTreeNodeFactoryProvider\norg.jd.gui.service.treenode.JavaModuleFileTreeNodeFactoryProvider\norg.jd.gui.service.treenode.JavaModulePackageTreeNodeFactoryProvider\norg.jd.gui.service.treenode.JsonFileTreeNodeFactoryProvider\norg.jd.gui.service.treenode.JspFileTreeNodeFactoryProvider\norg.jd.gui.service.treenode.KarFileTreeNodeFactoryProvider\norg.jd.gui.service.treenode.ManifestFileTreeNodeFactoryProvider\norg.jd.gui.service.treenode.MetainfDirectoryTreeNodeFactoryProvider\norg.jd.gui.service.treenode.MetainfServiceFileTreeNodeFactoryProvider\norg.jd.gui.service.treenode.ModuleInfoFileTreeNodeFactoryProvider\norg.jd.gui.service.treenode.PackageTreeNodeFactoryProvider\norg.jd.gui.service.treenode.PropertiesFileTreeNodeFactoryProvider\norg.jd.gui.service.treenode.SqlFileTreeNodeFactoryProvider\norg.jd.gui.service.treenode.SpiFileTreeNodeFactoryProvider\norg.jd.gui.service.treenode.TextFileTreeNodeFactoryProvider\norg.jd.gui.service.treenode.WarFileTreeNodeFactoryProvider\norg.jd.gui.service.treenode.WarPackageTreeNodeFactoryProvider\norg.jd.gui.service.treenode.WebinfLibDirectoryTreeNodeFactoryProvider\norg.jd.gui.service.treenode.WebXmlFileTreeNodeFactoryProvider\norg.jd.gui.service.treenode.XmlBasedFileTreeNodeFactoryProvider\norg.jd.gui.service.treenode.XmlFileTreeNodeFactoryProvider\norg.jd.gui.service.treenode.ZipFileTreeNodeFactoryProvider\norg.jd.gui.service.treenode.ImageFileTreeNodeFactoryProvider\n"
  },
  {
    "path": "services/src/main/resources/META-INF/services/org.jd.gui.spi.TypeFactory",
    "content": "org.jd.gui.service.type.ClassFileTypeFactoryProvider\norg.jd.gui.service.type.JavaFileTypeFactoryProvider\n"
  },
  {
    "path": "services/src/main/resources/META-INF/services/org.jd.gui.spi.UriLoader",
    "content": "org.jd.gui.service.uriloader.FileUriLoaderProvider\n"
  },
  {
    "path": "services/src/main/resources/rsyntaxtextarea/RSyntaxTextArea_License.txt",
    "content": "Copyright (c) 2012, Robert Futrell\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n    * Redistributions of source code must retain the above copyright\n      notice, this list of conditions and the following disclaimer.\n    * Redistributions in binary form must reproduce the above copyright\n      notice, this list of conditions and the following disclaimer in the\n      documentation and/or other materials provided with the distribution.\n    * Neither the name of the author nor the names of its contributors may\n      be used to endorse or promote products derived from this software\n      without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY\nDIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\nON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
  },
  {
    "path": "services/src/main/resources/rsyntaxtextarea/themes/eclipse.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE RSyntaxTheme SYSTEM \"theme.dtd\">\n\n<!--\n\tTheme that mimics Eclipse's defaults.\n\tSee theme.dtd and org.fife.ui.rsyntaxtextarea.Theme for more information.\n-->\n<RSyntaxTheme version=\"1.0\">\n\n   <!-- Omitting baseFont will use a system-appropriate monospaced. -->\n   <!--<baseFont family=\"...\" size=\"13\"/>-->\n    <baseFont size=\"12\"/>\n   \n   <!--  General editor colors. -->\n   <background color=\"ffffff\"/>\n   <caret color=\"000000\"/>\n   <selection bg=\"default\" fg=\"default\"/>\n   <currentLineHighlight color=\"e8f2fe\" fade=\"false\"/>\n   <marginLine fg=\"b0b4b9\"/>\n   <markAllHighlight color=\"6b8189\"/> <!-- TODO: Fix me -->\n   <markOccurrencesHighlight color=\"d4d4d4\" border=\"false\"/>\n   <matchedBracket fg=\"c0c0c0\" highlightBoth=\"false\" animate=\"false\"/>\n   <hyperlinks fg=\"0000ff\"/>\n   <secondaryLanguages>\n      <language index=\"1\" bg=\"fff0cc\"/>\n      <language index=\"2\" bg=\"dafeda\"/>\n      <language index=\"3\" bg=\"ffe0f0\"/>\n   </secondaryLanguages>\n   \n   <!-- Gutter styling. -->\n   <gutterBorder color=\"dddddd\"/>\n   <lineNumbers fg=\"787878\"/>\n   <foldIndicator fg=\"808080\" iconBg=\"ffffff\"/>\n   <iconRowHeader activeLineRange=\"3399ff\"/>\n   \n   <!-- Syntax tokens. -->\n   <tokenStyles>\n      <style token=\"IDENTIFIER\" fg=\"000000\"/>\n      <style token=\"RESERVED_WORD\" fg=\"7f0055\" bold=\"true\"/>\n      <style token=\"RESERVED_WORD_2\" fg=\"7f0055\" bold=\"true\"/>\n      <style token=\"ANNOTATION\" fg=\"808080\"/>\n      <style token=\"COMMENT_DOCUMENTATION\" fg=\"3f5fbf\"/>\n      <style token=\"COMMENT_EOL\" fg=\"3f7f5f\"/>\n      <style token=\"COMMENT_MULTILINE\" fg=\"3f7f5f\"/>\n      <style token=\"COMMENT_KEYWORD\" fg=\"7F9FBF\" bold=\"true\"/>\n      <style token=\"COMMENT_MARKUP\" fg=\"7f7f9f\"/>\n      <style token=\"DATA_TYPE\" fg=\"7f0055\" bold=\"true\"/>\n      <style token=\"FUNCTION\" fg=\"000000\"/>\n      <style token=\"LITERAL_BOOLEAN\" fg=\"7f0055\" bold=\"true\"/>\n      <style token=\"LITERAL_NUMBER_DECIMAL_INT\" fg=\"000000\"/>\n      <style token=\"LITERAL_NUMBER_FLOAT\" fg=\"000000\"/>\n      <style token=\"LITERAL_NUMBER_HEXADECIMAL\" fg=\"000000\"/>\n      <style token=\"LITERAL_STRING_DOUBLE_QUOTE\" fg=\"2900ff\"/>\n      <style token=\"LITERAL_CHAR\" fg=\"2900ff\"/>\n      <style token=\"LITERAL_BACKQUOTE\" fg=\"2900ff\"/>\n      <style token=\"MARKUP_TAG_DELIMITER\" fg=\"008080\"/>\n      <style token=\"MARKUP_TAG_NAME\" fg=\"3f7f7f\"/>\n      <style token=\"MARKUP_TAG_ATTRIBUTE\" fg=\"7f007f\"/>\n      <style token=\"MARKUP_TAG_ATTRIBUTE_VALUE\" fg=\"2a00ff\" italic=\"true\"/>\n      <style token=\"MARKUP_COMMENT\" fg=\"3f5fbf\"/>\n      <style token=\"MARKUP_DTD\" fg=\"008080\"/>\n      <style token=\"MARKUP_PROCESSING_INSTRUCTION\" fg=\"808080\"/>\n      <style token=\"MARKUP_CDATA\" fg=\"000000\"/>\n      <style token=\"MARKUP_CDATA_DELIMITER\" fg=\"008080\"/>\n      <style token=\"MARKUP_ENTITY_REFERENCE\" fg=\"2a00ff\"/>\n      <style token=\"OPERATOR\" fg=\"000000\"/>\n      <style token=\"PREPROCESSOR\" fg=\"808080\"/>\n      <style token=\"REGEX\" fg=\"008040\"/>\n      <style token=\"SEPARATOR\" fg=\"000000\"/>\n      <style token=\"VARIABLE\" fg=\"ff9900\" bold=\"true\"/>\n      <style token=\"WHITESPACE\" fg=\"000000\"/>\n      \n      <style token=\"ERROR_IDENTIFIER\" fg=\"000000\" bg=\"ffcccc\"/>\n      <style token=\"ERROR_NUMBER_FORMAT\" fg=\"000000\" bg=\"ffcccc\"/>\n      <style token=\"ERROR_STRING_DOUBLE\" fg=\"000000\" bg=\"ffcccc\"/>\n      <style token=\"ERROR_CHAR\" fg=\"000000\" bg=\"ffcccc\"/>\n   </tokenStyles>\n\n</RSyntaxTheme>\n"
  },
  {
    "path": "services/src/test/java/org/jd/gui/util/matcher/DescriptorMatcherTest.java",
    "content": "package org.jd.gui.util.matcher;\n\nimport junit.framework.TestCase;\nimport org.junit.Assert;\n\npublic class DescriptorMatcherTest extends TestCase {\n    public void testMatchFieldDescriptors() {\n        Assert.assertTrue(DescriptorMatcher.matchFieldDescriptors(\"?\", \"?\"));\n\n        Assert.assertTrue(DescriptorMatcher.matchFieldDescriptors(\"I\", \"I\"));\n        Assert.assertTrue(DescriptorMatcher.matchFieldDescriptors(\"?\", \"I\"));\n        Assert.assertTrue(DescriptorMatcher.matchFieldDescriptors(\"I\", \"?\"));\n\n        Assert.assertTrue(DescriptorMatcher.matchFieldDescriptors(\"Ltest/Test;\", \"Ltest/Test;\"));\n        Assert.assertTrue(DescriptorMatcher.matchFieldDescriptors(\"?\", \"Ltest/Test;\"));\n        Assert.assertTrue(DescriptorMatcher.matchFieldDescriptors(\"Ltest/Test;\", \"?\"));\n        Assert.assertTrue(DescriptorMatcher.matchFieldDescriptors(\"L*/Test;\", \"Ltest/Test;\"));\n        Assert.assertTrue(DescriptorMatcher.matchFieldDescriptors(\"Ltest/Test;\", \"L*/Test;\"));\n\n        Assert.assertTrue(DescriptorMatcher.matchFieldDescriptors(\"L*/Test;\", \"L*/Test;\"));\n        Assert.assertTrue(DescriptorMatcher.matchFieldDescriptors(\"?\", \"L*/Test;\"));\n        Assert.assertTrue(DescriptorMatcher.matchFieldDescriptors(\"L*/Test;\", \"?\"));\n\n        Assert.assertTrue(DescriptorMatcher.matchFieldDescriptors(\"[Z\", \"[Z\"));\n        Assert.assertTrue(DescriptorMatcher.matchFieldDescriptors(\"[Z\", \"?\"));\n        Assert.assertTrue(DescriptorMatcher.matchFieldDescriptors(\"?\", \"[Z\"));\n\n        Assert.assertTrue(DescriptorMatcher.matchFieldDescriptors(\"Ltest/Test;\", \"Ltest/Test;\"));\n        Assert.assertTrue(DescriptorMatcher.matchFieldDescriptors(\"Ltest/Test;\", \"?\"));\n        Assert.assertTrue(DescriptorMatcher.matchFieldDescriptors(\"?\", \"Ltest/Test;\"));\n\n        Assert.assertTrue(DescriptorMatcher.matchFieldDescriptors(\"[[[Ltest/Test;\", \"[[[Ltest/Test;\"));\n        Assert.assertTrue(DescriptorMatcher.matchFieldDescriptors(\"[[[Ltest/Test;\", \"?\"));\n        Assert.assertTrue(DescriptorMatcher.matchFieldDescriptors(\"?\", \"[[[Ltest/Test;\"));\n\n        Assert.assertTrue(DescriptorMatcher.matchFieldDescriptors(\"[[[L*/Test;\", \"[[[L*/Test;\"));\n        Assert.assertTrue(DescriptorMatcher.matchFieldDescriptors(\"[[[L*/Test;\", \"?\"));\n        Assert.assertTrue(DescriptorMatcher.matchFieldDescriptors(\"?\", \"[[[L*/Test;\"));\n    }\n\n    public void testMatchMethodDescriptors() {\n        Assert.assertFalse(DescriptorMatcher.matchMethodDescriptors(\"I\", \"I\"));\n\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"()I\", \"()I\"));\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"(*)?\", \"()I\"));\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"()I\", \"(*)?\"));\n\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"(I)I\", \"(I)I\"));\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"(*)?\", \"(I)I\"));\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"(I)I\", \"(*)?\"));\n\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"(IJ)I\", \"(IJ)I\"));\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"(*)?\", \"(IJ)I\"));\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"(IJ)I\", \"(*)?\"));\n\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"(Ltest/Test;)Ltest/Test;\", \"(Ltest/Test;)Ltest/Test;\"));\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"(*)?\", \"(Ltest/Test;)Ltest/Test;\"));\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"(Ltest/Test;)Ltest/Test;\", \"(*)?\"));\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"([[Ltest/Test;[[Ltest/Test;)Ltest/Test;\", \"([[L*/Test;[[L*/Test;)L*/Test;\"));\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"([[L*/Test;[[L*/Test;)L*/Test;\", \"([[Ltest/Test;[[Ltest/Test;)Ltest/Test;\"));\n\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"(Ltest/Test;Ltest/Test;)Ltest/Test;\", \"(Ltest/Test;Ltest/Test;)Ltest/Test;\"));\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"(*)?\", \"(Ltest/Test;Ltest/Test;)Ltest/Test;\"));\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"(Ltest/Test;Ltest/Test;)Ltest/Test;\", \"(*)?\"));\n\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"([[Ltest/Test;[[Ltest/Test;)Ltest/Test;\", \"([[Ltest/Test;[[Ltest/Test;)Ltest/Test;\"));\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"(*)?\", \"([[Ltest/Test;[[Ltest/Test;)Ltest/Test;\"));\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"([[Ltest/Test;[[Ltest/Test;)Ltest/Test;\", \"(*)?\"));\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"([[L*/Test;[[L*/Test;)L*/Test;\", \"([[Ltest/Test;[[Ltest/Test;)Ltest/Test;\"));\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"([[Ltest/Test;[[Ltest/Test;)Ltest/Test;\", \"([[L*/Test;[[L*/Test;)L*/Test;\"));\n\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"(L*/Test;)L*/Test;\", \"(L*/Test;)L*/Test;\"));\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"(*)?\", \"(L*/Test;)L*/Test;\"));\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"(L*/Test;)L*/Test;\", \"(*)?\"));\n\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"(L*/Test;L*/Test;)L*/Test;\", \"(L*/Test;L*/Test;)L*/Test;\"));\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"(*)?\", \"(L*/Test;L*/Test;)L*/Test;\"));\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"(Ltest/Test;Ltest/Test;)Ltest/Test;\", \"(*)?\"));\n\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"([[L*/Test;[[L*/Test;)L*/Test;\", \"([[L*/Test;[[L*/Test;)L*/Test;\"));\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"(*)?\", \"([[L*/Test;[[L*/Test;)L*/Test;\"));\n        Assert.assertTrue(DescriptorMatcher.matchMethodDescriptors(\"([[L*/Test;[[L*/Test;)L*/Test;\", \"(*)?\"));\n    }\n}\n"
  },
  {
    "path": "services/src/test/java/org/jd/gui/view/component/ClassFilePageTest.java",
    "content": "package org.jd.gui.view.component;\n\nimport junit.framework.TestCase;\nimport org.fife.ui.rsyntaxtextarea.DocumentRange;\nimport org.junit.Assert;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.TreeMap;\n\npublic class ClassFilePageTest extends TestCase {\n\n    public HashMap<String, TypePage.DeclarationData> initDeclarations() {\n        TypePage.DeclarationData data = new TypePage.DeclarationData(0, 1, \"Test\", \"test\", \"I\");\n        HashMap<String, TypePage.DeclarationData> declarations = new HashMap<>();\n\n        // Init type declarations\n        declarations.put(\"Test\", data);\n        declarations.put(\"test/Test\", data);\n\n        // Init field declarations\n        declarations.put(\"Test-attributeInt-I\", data);\n        declarations.put(\"Test-attributeBoolean-Z\", data);\n        declarations.put(\"Test-attributeArrayBoolean-[[Z\", data);\n        declarations.put(\"Test-attributeString-Ljava/lang/String;\", data);\n\n        declarations.put(\"test/Test-attributeInt-I\", data);\n        declarations.put(\"test/Test-attributeBoolean-Z\", data);\n        declarations.put(\"test/Test-attributeArrayBoolean-[[Z\", data);\n        declarations.put(\"test/Test-attributeString-Ljava/lang/String;\", data);\n\n        // Init method declarations\n        declarations.put(\"Test-getInt-()I\", data);\n        declarations.put(\"Test-getString-()Ljava/lang/String;\", data);\n        declarations.put(\"Test-add-(JJ)J\", data);\n        declarations.put(\"Test-createBuffer-(I)[C\", data);\n\n        declarations.put(\"test/Test-getInt-()I\", data);\n        declarations.put(\"test/Test-getString-()Ljava/lang/String;\", data);\n        declarations.put(\"test/Test-add-(JJ)J\", data);\n        declarations.put(\"test/Test-createBuffer-(I)[C\", data);\n\n        return declarations;\n    }\n\n    public TreeMap<Integer, HyperlinkPage.HyperlinkData> initHyperlinks() {\n        TreeMap<Integer, HyperlinkPage.HyperlinkData> hyperlinks = new TreeMap<>();\n\n        hyperlinks.put(0, new TypePage.HyperlinkReferenceData(0, 1, new TypePage.ReferenceData(\"java/lang/Integer\", \"MAX_VALUE\", \"I\", \"Test\")));\n        hyperlinks.put(1, new TypePage.HyperlinkReferenceData(0, 1, new TypePage.ReferenceData(\"java/lang/Integer\", \"toString\", \"()Ljava/lang/String;\", \"Test\")));\n\n        return hyperlinks;\n    }\n\n    public ArrayList<TypePage.StringData> initStrings() {\n        ArrayList<TypePage.StringData> strings = new ArrayList<>();\n\n        strings.add(new TypePage.StringData(0, 3, \"abc\", \"Test\"));\n\n        return strings;\n    }\n\n    public void testMatchFragmentAndAddDocumentRange() {\n        HashMap<String, TypePage.DeclarationData> declarations = initDeclarations();\n        ArrayList<DocumentRange> ranges = new ArrayList<>();\n\n        ranges.clear();\n        ClassFilePage.matchFragmentAndAddDocumentRange(\"Test-attributeBoolean-Z\", declarations, ranges);\n        Assert.assertTrue(ranges.size() == 1);\n\n        ranges.clear();\n        ClassFilePage.matchFragmentAndAddDocumentRange(\"test/Test-attributeBoolean-Z\", declarations, ranges);\n        Assert.assertTrue(ranges.size() == 1);\n\n        ranges.clear();\n        ClassFilePage.matchFragmentAndAddDocumentRange(\"*/Test-attributeBoolean-Z\", declarations, ranges);\n        Assert.assertTrue(ranges.size() == 2);\n\n        ranges.clear();\n        ClassFilePage.matchFragmentAndAddDocumentRange(\"Test-createBuffer-(I)[C\", declarations, ranges);\n        Assert.assertTrue(ranges.size() == 1);\n\n        ranges.clear();\n        ClassFilePage.matchFragmentAndAddDocumentRange(\"test/Test-createBuffer-(I)[C\", declarations, ranges);\n        Assert.assertTrue(ranges.size() == 1);\n\n        ranges.clear();\n        ClassFilePage.matchFragmentAndAddDocumentRange(\"*/Test-getString-(*)?\", declarations, ranges);\n        Assert.assertTrue(ranges.size() == 2);\n\n        ranges.clear();\n        ClassFilePage.matchFragmentAndAddDocumentRange(\"test/Test-add-(?J)?\", declarations, ranges);\n        Assert.assertTrue(ranges.size() == 1);\n    }\n\n    public void testMatchQueryAndAddDocumentRange() {\n        HashMap<String, String> parameters = new HashMap<>();\n        HashMap<String, TypePage.DeclarationData> declarations = initDeclarations();\n        TreeMap<Integer, HyperlinkPage.HyperlinkData> hyperlinks = initHyperlinks();\n        ArrayList<TypePage.StringData> strings = initStrings();\n        ArrayList<DocumentRange> ranges = new ArrayList<>();\n\n        parameters.put(\"highlightPattern\", \"ab\");\n        parameters.put(\"highlightFlags\", \"s\");\n\n        parameters.put(\"highlightScope\", null);\n        ranges.clear();\n        ClassFilePage.matchQueryAndAddDocumentRange(parameters, declarations, hyperlinks, strings, ranges);\n        Assert.assertTrue(ranges.size() == 1);\n\n        parameters.put(\"highlightScope\", \"\");\n        ranges.clear();\n        ClassFilePage.matchQueryAndAddDocumentRange(parameters, declarations, hyperlinks, strings, ranges);\n        Assert.assertTrue(ranges.size() == 1);\n\n        parameters.put(\"highlightScope\", \"Test\");\n        ranges.clear();\n        ClassFilePage.matchQueryAndAddDocumentRange(parameters, declarations, hyperlinks, strings, ranges);\n        Assert.assertTrue(ranges.size() == 1);\n    }\n\n    public void testMatchScope() {\n        Assert.assertTrue(ClassFilePage.matchScope(null, \"java/lang/String\"));\n        Assert.assertTrue(ClassFilePage.matchScope(\"\", \"java/lang/String\"));\n\n        Assert.assertTrue(ClassFilePage.matchScope(\"java/lang/String\", \"java/lang/String\"));\n        Assert.assertTrue(ClassFilePage.matchScope(\"*/lang/String\", \"java/lang/String\"));\n        Assert.assertTrue(ClassFilePage.matchScope(\"*/String\", \"java/lang/String\"));\n\n        Assert.assertTrue(ClassFilePage.matchScope(null, \"Test\"));\n        Assert.assertTrue(ClassFilePage.matchScope(\"\", \"Test\"));\n\n        Assert.assertTrue(ClassFilePage.matchScope(\"Test\", \"Test\"));\n        Assert.assertTrue(ClassFilePage.matchScope(\"*/Test\", \"Test\"));\n    }\n}\n"
  },
  {
    "path": "services/src/test/java/org/jd/gui/view/component/JavaFilePageTest.java",
    "content": "package org.jd.gui.view.component;\n\nimport junit.framework.TestCase;\n\nimport java.util.HashMap;\n\npublic class JavaFilePageTest extends TestCase {\n\n    public HashMap<String, TypePage.DeclarationData> initDeclarations() {\n        TypePage.DeclarationData data = new TypePage.DeclarationData(0, 1, \"Test\", \"test\", \"I\");\n        HashMap<String, TypePage.DeclarationData> declarations = new HashMap<>();\n\n        // Init type declarations\n        declarations.put(\"Test\", data);\n        declarations.put(\"test/Test\", data);\n        declarations.put(\"*/Test\", data);\n\n        // Init field declarations\n        declarations.put(\"Test-attributeInt-I\", data);\n        declarations.put(\"Test-attributeBoolean-Z\", data);\n        declarations.put(\"Test-attributeArrayBoolean-[[Z\", data);\n        declarations.put(\"Test-attributeString-Ljava/lang/String;\", data);\n\n        declarations.put(\"test/Test-attributeInt-I\", data);\n        declarations.put(\"test/Test-attributeBoolean-Z\", data);\n        declarations.put(\"test/Test-attributeArrayBoolean-[[Z\", data);\n        declarations.put(\"test/Test-attributeString-Ljava/lang/String;\", data);\n\n        declarations.put(\"*/Test-attributeBoolean-?\", data);\n        declarations.put(\"*/Test-attributeBoolean-Z\", data);\n        declarations.put(\"test/Test-attributeBoolean-?\", data);\n\n        // Init method declarations\n        declarations.put(\"*/Test-getInt-()I\", data);\n        declarations.put(\"*/Test-getString-()Ljava/lang/String;\", data);\n        declarations.put(\"*/Test-add-(JJ)J\", data);\n        declarations.put(\"*/Test-createBuffer-(I)[C\", data);\n\n        declarations.put(\"test/Test-getInt-(*)?\", data);\n        declarations.put(\"test/Test-getString-(*)?\", data);\n        declarations.put(\"test/Test-add-(*)?\", data);\n        declarations.put(\"test/Test-createBuffer-(*)?\", data);\n\n        declarations.put(\"*/Test-getInt-(*)?\", data);\n        declarations.put(\"*/Test-getString-(*)?\", data);\n        declarations.put(\"*/Test-add-(*)?\", data);\n        declarations.put(\"*/Test-createBuffer-(*)?\", data);\n\n        return declarations;\n    }\n\n    public void testMatchFragmentAndAddDocumentRange() {}\n\n    public void testMatchQueryAndAddDocumentRange() {}\n\n    public void testMatchScope() {}\n}\n"
  },
  {
    "path": "settings.gradle",
    "content": "include 'api', 'app', 'services'\n\nrootProject.name='jd-gui'\n\n"
  },
  {
    "path": "src/linux/resources/jd-gui.desktop",
    "content": "[Desktop Entry]\nComment=Java Decompiler JD-GUI\nTerminal=false\nName=JD-GUI\nExec=java -jar /opt/jd-gui/jd-gui.jar\nType=Application\nIcon=jd-gui\nMimeType=application/java;application/java-vm;application/java-archive\nStartupWMClass=org-jd-gui-App\n"
  },
  {
    "path": "src/osx/resources/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n\t<dict>\n\t\t<key>CFBundleDevelopmentRegion</key>\t\t<string>English</string>\n\t\t<key>CFBundleExecutable</key>\t\t\t\t<string>universalJavaApplicationStub.sh</string>\n\t\t<key>CFBundleName</key>\t\t\t\t\t\t<string>JD-GUI</string>\n\t\t<key>CFBundleGetInfoString</key>\t\t\t<string>JD-GUI version ${VERSION}, Copyright 2008, 2019 Emmanuel Dupuy</string>\n\t\t<key>CFBundleIconFile</key>\t\t\t\t\t<string>jd-gui.icns</string>\n\t\t<key>CFBundleIdentifier</key>\t\t\t\t<string>jd.jd-gui</string>\n\t\t<key>CFBundleInfoDictionaryVersion</key>\t<string>6.0</string>\n\t\t<key>CFBundlePackageType</key>\t\t\t\t<string>APPL</string>\n\t\t<key>CFBundleLongVersionString</key>\t\t<string>${VERSION}, Copyright 2008, 2019 Emmanuel Dupuy</string>\n\t\t<key>CFBundleShortVersionString</key>\t\t<string>${VERSION}</string>\n\t\t<key>CSResourcesFileMapped</key>\t\t\t<true/>\n\t\t<key>LSRequiresCarbon</key>\t\t\t\t\t<true/>\n\t\t<key>NSHumanReadableCopyright</key>\t\t\t<string>Copyright 2008, 2019 Emmanuel Dupuy</string>\n\t\t<key>NSPrincipalClass</key>\t\t\t\t\t<string>NSApplication</string>\n\t\t<key>NSHighResolutionCapable</key>\t\t\t<true/>\n\t\t<key>CFBundleDocumentTypes</key>\n\t\t<array>\n\t\t\t<dict>\n\t\t\t\t<key>CFBundleTypeExtensions</key>\n\t\t\t\t<array>\n\t\t\t\t\t<string>class</string>\n\t\t\t\t</array>\n\t\t\t\t<key>CFBundleTypeMIMETypes</key>\n\t\t\t\t<array>\n\t\t\t\t\t<string>application/java</string>\n\t\t\t\t</array>\n\t\t\t\t<key>CFBundleTypeRole</key>\t\t\t<string>Viewer</string>\n\t\t\t\t<key>CFBundleTypeName</key>\t\t\t<string>Class File</string>\n\t\t\t\t<key>LSIsAppleDefaultForType</key>\t<true/>\n\t\t\t\t<key>LSTypeIsPackage</key>\t\t\t<false/>\n\t\t\t</dict>\n\t\t\t<dict>\n\t\t\t\t<key>CFBundleTypeExtensions</key>\n\t\t\t\t<array>\n\t\t\t\t\t<string>java</string>\n\t\t\t\t</array>\n\t\t\t\t<key>CFBundleTypeMIMETypes</key>\n\t\t\t\t<array>\n\t\t\t\t\t<string>text/plain</string>\n\t\t\t\t</array>\n\t\t\t\t<key>CFBundleTypeRole</key>\t\t\t<string>Viewer</string>\n\t\t\t\t<key>CFBundleTypeName</key>\t\t\t<string>Java File</string>\n\t\t\t\t<key>LSIsAppleDefaultForType</key>\t<false/>\n\t\t\t\t<key>LSTypeIsPackage</key>\t\t\t<false/>\n\t\t\t</dict>\n\t\t\t<dict>\n\t\t\t\t<key>CFBundleTypeExtensions</key>\n\t\t\t\t<array>\n\t\t\t\t\t<string>jar</string>\n\t\t\t\t</array>\n\t\t\t\t<key>CFBundleTypeMIMETypes</key>\n\t\t\t\t<array>\n\t\t\t\t\t<string>application/java-archive</string>\n\t\t\t\t</array>\n\t\t\t\t<key>CFBundleTypeName</key>\t\t\t<string>Jar File</string>\n\t\t\t\t<key>CFBundleTypeRole</key>\t\t\t<string>Viewer</string>\n\t\t\t\t<key>LSIsAppleDefaultForType</key>\t<false/>\n\t\t\t\t<key>LSTypeIsPackage</key>\t\t\t<false/>\n\t\t\t</dict>\n\t\t\t<dict>\n\t\t\t\t<key>CFBundleTypeExtensions</key>\n\t\t\t\t<array>\n\t\t\t\t\t<string>war</string>\n\t\t\t\t</array>\n\t\t\t\t<key>CFBundleTypeMIMETypes</key>\n\t\t\t\t<array>\n\t\t\t\t\t<string>application/java-archive</string>\n\t\t\t\t</array>\n\t\t\t\t<key>CFBundleTypeName</key>\t\t\t<string>War File</string>\n\t\t\t\t<key>CFBundleTypeRole</key>\t\t\t<string>Viewer</string>\n\t\t\t\t<key>LSIsAppleDefaultForType</key>\t<false/>\n\t\t\t\t<key>LSTypeIsPackage</key>\t\t\t<false/>\n\t\t\t</dict>\n\t\t\t<dict>\n\t\t\t\t<key>CFBundleTypeExtensions</key>\n\t\t\t\t<array>\n\t\t\t\t\t<string>ear</string>\n\t\t\t\t</array>\n\t\t\t\t<key>CFBundleTypeMIMETypes</key>\n\t\t\t\t<array>\n\t\t\t\t\t<string>application/java-archive</string>\n\t\t\t\t</array>\n\t\t\t\t<key>CFBundleTypeName</key>\t\t\t<string>Ear File</string>\n\t\t\t\t<key>CFBundleTypeRole</key>\t\t\t<string>Viewer</string>\n\t\t\t\t<key>LSIsAppleDefaultForType</key>\t<false/>\n\t\t\t\t<key>LSTypeIsPackage</key>\t\t\t<false/>\n\t\t\t</dict>\n\t\t\t<dict>\n\t\t\t\t<key>CFBundleTypeExtensions</key>\n\t\t\t\t<array>\n\t\t\t\t\t<string>aar</string>\n\t\t\t\t</array>\n\t\t\t\t<key>CFBundleTypeName</key>\t\t\t<string>Android archive File</string>\n\t\t\t\t<key>CFBundleTypeRole</key>\t\t\t<string>Viewer</string>\n\t\t\t\t<key>LSIsAppleDefaultForType</key>\t<false/>\n\t\t\t\t<key>LSTypeIsPackage</key>\t\t\t<false/>\n\t\t\t</dict>\n\t\t\t<dict>\n\t\t\t\t<key>CFBundleTypeExtensions</key>\n\t\t\t\t<array>\n\t\t\t\t\t<string>jmod</string>\n\t\t\t\t</array>\n\t\t\t\t<key>CFBundleTypeMIMETypes</key>\n\t\t\t\t<array>\n\t\t\t\t\t<string>application/java-archive</string>\n\t\t\t\t</array>\n\t\t\t\t<key>CFBundleTypeName</key>\t\t\t<string>Java module File</string>\n\t\t\t\t<key>CFBundleTypeRole</key>\t\t\t<string>Viewer</string>\n\t\t\t\t<key>LSIsAppleDefaultForType</key>\t<false/>\n\t\t\t\t<key>LSTypeIsPackage</key>\t\t\t<false/>\n\t\t\t</dict>\n\t\t\t<dict>\n\t\t\t\t<key>CFBundleTypeExtensions</key>\n\t\t\t\t<array>\n\t\t\t\t\t<string>zip</string>\n\t\t\t\t</array>\n\t\t\t\t<key>CFBundleTypeMIMETypes</key>\n\t\t\t\t<array>\n\t\t\t\t\t<string>application/zip</string>\n\t\t\t\t</array>\n\t\t\t\t<key>CFBundleTypeName</key>\t\t\t<string>Zip File</string>\n\t\t\t\t<key>CFBundleTypeRole</key>\t\t\t<string>Viewer</string>\n\t\t\t\t<key>LSIsAppleDefaultForType</key>\t<false/>\n\t\t\t\t<key>LSTypeIsPackage</key>\t\t\t<false/>\n\t\t\t</dict>\n\t\t\t<dict>\n\t\t\t\t<key>CFBundleTypeExtensions</key>\n\t\t\t\t<array>\n\t\t\t\t\t<string>log</string>\n\t\t\t\t\t<string>txt</string>\n\t\t\t\t</array>\n\t\t\t\t<key>CFBundleTypeMIMETypes</key>\n\t\t\t\t<array>\n\t\t\t\t\t<string>text/plain</string>\n\t\t\t\t</array>\n\t\t\t\t<key>CFBundleTypeName</key>\t\t\t<string>Log File</string>\n\t\t\t\t<key>CFBundleTypeRole</key>\t\t\t<string>Viewer</string>\n\t\t\t\t<key>LSIsAppleDefaultForType</key>\t<false/>\n\t\t\t\t<key>LSTypeIsPackage</key>\t\t\t<false/>\n\t\t\t</dict>\n\t\t</array>\n\t\t<key>JavaX</key>\n\t\t<dict>\n\t\t\t<key>MainClass</key>\t\t\t\t\t<string>org.jd.gui.OsxApp</string>\n\t\t\t<key>JVMVersion</key>\t\t\t\t\t<string>1.8+</string>\n\t\t\t<key>ClassPath</key>\t\t\t\t\t<string>\\$JAVAROOT/${JAR}</string>\n\t\t\t<key>WorkingDirectory</key>\t\t\t\t<string>\\$JAVAROOT</string>\n\t\t\t<key>Properties</key>\n\t\t\t<dict>\n\t\t\t\t<key>apple.laf.useScreenMenuBar</key>\n\t\t\t\t<string>true</string>\n\t\t\t</dict>\n\t\t\t<key>VMOptions</key>\t\t\t\t\t<string>-Xms512m</string>\n\t\t</dict>\n\t</dict>\n</plist>\n"
  },
  {
    "path": "src/osx/resources/universalJavaApplicationStub.sh",
    "content": "#!/bin/sh\n##################################################################################\n#                                                                                #\n# universalJavaApplicationStub                                                   #\n#                                                                                #\n#                                                                                #\n# A shellscript JavaApplicationStub for Java Apps on Mac OS X                    #\n# that works with both Apple's and Oracle's plist format.                        #\n#                                                                                #\n# Inspired by Ian Roberts stackoverflow answer                                   #\n# at http://stackoverflow.com/a/17546508/1128689                                 #\n#                                                                                #\n#                                                                                #\n# @author    Tobias Fischer                                                      #\n# @url       https://github.com/tofi86/universalJavaApplicationStub              #\n# @date      2015-05-15                                                          #\n# @version   0.9.0                                                               #\n#                                                                                #\n#                                                                                #\n##################################################################################\n#                                                                                #\n#                                                                                #\n# The MIT License (MIT)                                                          #\n#                                                                                #\n# Copyright (c) 2015 Tobias Fischer                                              #\n#                                                                                #\n# Permission is hereby granted, free of charge, to any person obtaining a copy   #\n# of this software and associated documentation files (the \"Software\"), to deal  #\n# in the Software without restriction, including without limitation the rights   #\n# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell      #\n# copies of the Software, and to permit persons to whom the Software is          #\n# furnished to do so, subject to the following conditions:                       #\n#                                                                                #\n# The above copyright notice and this permission notice shall be included in all #\n# copies or substantial portions of the Software.                                #\n#                                                                                #\n# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR     #\n# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,       #\n# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE    #\n# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER         #\n# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  #\n# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE  #\n# SOFTWARE.                                                                      #\n#                                                                                #\n##################################################################################\n\n\n\n\n#\n# resolve symlinks\n#####################\n\nPRG=$0\n\nwhile [ -h \"$PRG\" ]; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '^.*-> \\(.*\\)$' 2>/dev/null`\n    if expr \"$link\" : '^/' 2> /dev/null >/dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=\"`dirname \"$PRG\"`/$link\"\n    fi\ndone\n\n# set the directory abspath of the current shell script\nPROGDIR=`dirname \"$PRG\"`\n\n\n\n\n#\n# set files and folders\n############################################\n\n# the absolute path of the app package\ncd \"$PROGDIR\"/../../\nAppPackageFolder=`pwd`\n\n# the base path of the app package\ncd ..\nAppPackageRoot=`pwd`\n\n# set Apple's Java folder\nAppleJavaFolder=\"${AppPackageFolder}\"/Contents/Resources/Java\n\n# set Apple's Resources folder\nAppleResourcesFolder=\"${AppPackageFolder}\"/Contents/Resources\n\n# set Oracle's Java folder\nOracleJavaFolder=\"${AppPackageFolder}\"/Contents/Java\n\n# set Oracle's Resources folder\nOracleResourcesFolder=\"${AppPackageFolder}\"/Contents/Resources\n\n# set path to Info.plist in bundle\nInfoPlistFile=\"${AppPackageFolder}\"/Contents/Info.plist\n\n# set the default JVM Version to a null string\nJVMVersion=\"\"\n\n\n\n#\n# read Info.plist and extract JVM options\n############################################\n\n\n# read the program name from CFBundleName\nCFBundleName=`/usr/libexec/PlistBuddy -c \"print :CFBundleName\" \"${InfoPlistFile}\"`\n\n# read the icon file name\nCFBundleIconFile=`/usr/libexec/PlistBuddy -c \"print :CFBundleIconFile\" \"${InfoPlistFile}\"`\n\n\n# check Info.plist for Apple style Java keys -> if key :Java is present, parse in apple mode\n/usr/libexec/PlistBuddy -c \"print :Java\" \"${InfoPlistFile}\" > /dev/null 2>&1\nexitcode=$?\nJavaKey=\":Java\"\n\n# if no :Java key is present, check Info.plist for universalJavaApplication style JavaX keys -> if key :JavaX is present, parse in apple mode\nif [ $exitcode -ne 0 ]; then\n\t/usr/libexec/PlistBuddy -c \"print :JavaX\" \"${InfoPlistFile}\" > /dev/null 2>&1\n\texitcode=$?\n\tJavaKey=\":JavaX\"\nfi\n\n\n# read Info.plist in Apple style if exit code returns 0 (true, :Java key is present)\nif [ $exitcode -eq 0 ]; then\n\n\t# set Java and Resources folder\n\tJavaFolder=\"${AppleJavaFolder}\"\n\tResourcesFolder=\"${AppleResourcesFolder}\"\n\n\tAPP_PACKAGE=\"${AppPackageFolder}\"\n\tJAVAROOT=\"${AppleJavaFolder}\"\n\tUSER_HOME=\"$HOME\"\n\n\n\t# read the Java WorkingDirectory\n\tJVMWorkDir=`/usr/libexec/PlistBuddy -c \"print ${JavaKey}:WorkingDirectory\" \"${InfoPlistFile}\" 2> /dev/null | xargs`\n\t\n\t# set Working Directory based upon Plist info\n\tif [[ ! -z ${JVMWorkDir} ]]; then\n\t\tWorkingDirectory=\"${JVMWorkDir}\"\n\telse\n\t\t# AppPackageRoot is the standard WorkingDirectory when the script is started\n\t\tWorkingDirectory=\"${AppPackageRoot}\"\n\tfi\n\n\t# expand variables $APP_PACKAGE, $JAVAROOT, $USER_HOME\n\tWorkingDirectory=`eval \"echo ${WorkingDirectory}\"`\n\n\n\t# read the MainClass name\n\tJVMMainClass=`/usr/libexec/PlistBuddy -c \"print ${JavaKey}:MainClass\" \"${InfoPlistFile}\" 2> /dev/null`\n\n\t# read the JVM Options\n\tJVMOptions=`/usr/libexec/PlistBuddy -c \"print ${JavaKey}:Properties\" \"${InfoPlistFile}\" 2> /dev/null | grep \" =\" | sed 's/^ */-D/g' | tr '\\n' ' ' | sed 's/  */ /g' | sed 's/ = /=/g' | xargs`\n\n\t# read StartOnMainThread\n\tJVMStartOnMainThread=`/usr/libexec/PlistBuddy -c \"print ${JavaKey}:StartOnMainThread\" \"${InfoPlistFile}\" 2> /dev/null`\n\tif [ \"${JVMStartOnMainThread}\" == \"true\" ]; then\n\t\techo ${JVMStartOnMainThread} > ~/Desktop/test.txt\n\t\tJVMOptions+=\" -XstartOnFirstThread\"\n\tfi\n\n\t# read the ClassPath in either Array or String style\n\tJVMClassPath_RAW=`/usr/libexec/PlistBuddy -c \"print ${JavaKey}:ClassPath\" \"${InfoPlistFile}\" 2> /dev/null`\n\tif [[ $JVMClassPath_RAW == *Array* ]] ; then\n\t\tJVMClassPath=.`/usr/libexec/PlistBuddy -c \"print ${JavaKey}:ClassPath\" \"${InfoPlistFile}\" 2> /dev/null | grep \"    \" | sed 's/^ */:/g' | tr -d '\\n' | xargs`\n\telse\n\t\tJVMClassPath=${JVMClassPath_RAW}\n\tfi\n\t# expand variables $APP_PACKAGE, $JAVAROOT, $USER_HOME\n\tJVMClassPath=`eval \"echo ${JVMClassPath}\"`\n\n\t# read the JVM Default Options\n\tJVMDefaultOptions=`/usr/libexec/PlistBuddy -c \"print ${JavaKey}:VMOptions\" \"${InfoPlistFile}\" 2> /dev/null | xargs`\n\n\t# read the JVM Arguments\n\tJVMArguments=`/usr/libexec/PlistBuddy -c \"print ${JavaKey}:Arguments\" \"${InfoPlistFile}\" 2> /dev/null | xargs`\n\n    # read the Java version we want to find\n    JVMVersion=`/usr/libexec/PlistBuddy -c \"print ${JavaKey}:JVMVersion\" \"${InfoPlistFile}\" 2> /dev/null | xargs`\n\n# read Info.plist in Oracle style\nelse\n\n\t# set Working Directory and Java and Resources folder\n\tJavaFolder=\"${OracleJavaFolder}\"\n\tResourcesFolder=\"${OracleResourcesFolder}\"\n\tWorkingDirectory=\"${OracleJavaFolder}\"\n\n\tAPP_ROOT=\"${AppPackageFolder}\"\n\n\t# read the MainClass name\n\tJVMMainClass=`/usr/libexec/PlistBuddy -c \"print :JVMMainClassName\" \"${InfoPlistFile}\" 2> /dev/null`\n\n\t# read the JVM Options\n\tJVMOptions=`/usr/libexec/PlistBuddy -c \"print :JVMOptions\" \"${InfoPlistFile}\" 2> /dev/null | grep \" -\" | tr -d '\\n' | sed 's/  */ /g' | xargs`\n\t# replace occurances of $APP_ROOT with it's content\n\tJVMOptions=`eval \"echo ${JVMOptions}\"`\n\n\tJVMClassPath=\"${JavaFolder}/*\"\n\n\t# read the JVM Default Options\n\tJVMDefaultOptions=`/usr/libexec/PlistBuddy -c \"print :JVMDefaultOptions\" \"${InfoPlistFile}\" 2> /dev/null | grep -o \"\\-.*\" | tr -d '\\n' | xargs`\n\n\t# read the JVM Arguments\n\tJVMArguments=`/usr/libexec/PlistBuddy -c \"print :JVMArguments\" \"${InfoPlistFile}\" 2> /dev/null | tr -d '\\n' | sed -E 's/Array \\{ *(.*) *\\}/\\1/g' | sed 's/  */ /g' | xargs`\n\t# replace occurances of $APP_ROOT with it's content\n\tJVMArguments=`eval \"echo ${JVMArguments}\"`\nfi\n\n\n\n\n#\n# find installed Java versions\n#################################\n\n# first check system variable \"$JAVA_HOME\"\nif [ -n \"$JAVA_HOME\" ] ; then\n\tJAVACMD=\"$JAVA_HOME/bin/java\"\n\t\n# check for specified JVMversion in \"/usr/libexec/java_home\" symlinks\nelif [ ! -z ${JVMVersion} ] && [ -x /usr/libexec/java_home ] && /usr/libexec/java_home -F; then\n\n\tif /usr/libexec/java_home -F -v ${JVMVersion}; then\n\t\tJAVACMD=\"`/usr/libexec/java_home -F -v ${JVMVersion} 2> /dev/null`/bin/java\"\n\telse\n\t\t# display error message with applescript\n\t\tosascript -e \"tell application \\\"System Events\\\" to display dialog \\\"ERROR launching '${CFBundleName}'\\n\\nNo suitable Java version found on your system!\\nThis program requires Java ${JVMVersion}\\nMake sure you install the required Java version.\\\" with title \\\"${CFBundleName}\\\" buttons {\\\" OK \\\"} default button 1 with icon path to resource \\\"${CFBundleIconFile}\\\" in bundle (path to me)\"\n\t\t# exit with error\n\t\texit 3\n\tfi\n\n# otherwise check \"/usr/libexec/java_home\" symlinks\nelif [ -x /usr/libexec/java_home ] && /usr/libexec/java_home -F; then\n\tJAVACMD=\"`/usr/libexec/java_home 2> /dev/null`/bin/java\"\n\n# otherwise check Java standard symlink (old Apple Java)\nelif [ -h /Library/Java/Home ]; then\n\tJAVACMD=\"/Library/Java/Home/bin/java\"\n\n# fallback: public JRE plugin (Oracle Java)\nelse\n\tJAVACMD=\"/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java\"\nfi\n\n# fallback fallback: /usr/bin/java\n# but this would prompt to install deprecated Apple Java 6\n\n\n\n\n#\n# execute JAVA commandline and do some pre-checks\n####################################################\n\n# display error message if MainClassName is empty\nif [ -z ${JVMMainClass} ]; then\n\t# display error message with applescript\n\tosascript -e \"tell application \\\"System Events\\\" to display dialog \\\"ERROR launching '${CFBundleName}'!\\n\\n'MainClass' isn't specified!\\nJava application cannot be started!\\\" with title \\\"${CFBundleName}\\\" buttons {\\\" OK \\\"} default button 1 with icon path to resource \\\"${CFBundleIconFile}\\\" in bundle (path to me)\"\n\t# exit with error\n\texit 2\n\n\n# check whether $JAVACMD is a file and executable\nelif [ -f \"$JAVACMD\" ] && [ -x \"$JAVACMD\" ] ; then\n\n\t# enable drag&drop to the dock icon\n\texport CFProcessPath=\"$0\"\n\n\t# change to Working Directory based upon Apple/Oracle Plist info\n\tcd \"${WorkingDirectory}\"\n\n\t# execute Java and set\n\t#\t- classpath\n\t#\t- dock icon\n\t#\t- application name\n\t#\t- JVM options\n\t#\t- JVM default options\n\t#\t- main class\n\t#\t- JVM arguments\n\texec \"$JAVACMD\" \\\n\t\t\t-cp \"${JVMClassPath}\" \\\n\t\t\t-Xdock:icon=\"${ResourcesFolder}/${CFBundleIconFile}\" \\\n\t\t\t-Xdock:name=\"${CFBundleName}\" \\\n\t\t\t${JVMOptions:+$JVMOptions }\\\n\t\t\t${JVMDefaultOptions:+$JVMDefaultOptions }\\\n\t\t\t${JVMMainClass}\\\n\t\t\t${JVMArguments:+ $JVMArguments}\n\n\nelse\n\n\t# display error message with applescript\n\tosascript -e \"tell application \\\"System Events\\\" to display dialog \\\"ERROR launching '${CFBundleName}'!\\n\\nYou need to have JAVA installed on your Mac!\\nVisit http://java.com for more information...\\\" with title \\\"${CFBundleName}\\\" buttons {\\\" OK \\\"} default button 1 with icon path to resource \\\"${CFBundleIconFile}\\\" in bundle (path to me)\"\n\n\t# and open java.com\n\topen http://java.com\n\n\t# exit with error\n\texit 1\nfi\n"
  },
  {
    "path": "src/proguard/resources/proguard.config.txt",
    "content": "# java -jar proguard.jar @proguard.config.txt\n\n#-injars       jd-gui-1.4.2.jar\n#-outjars      jd-gui-1.4.2-min.jar\n#-libraryjars  C:/Program Files/Java/jre1.8.0_121/lib/rt.jar\n#-printmapping myapplication.map\n\n-keep public class org.jd.gui.App {\n    public static void main(java.lang.String[]);\n}\n\n-keep public class org.jd.gui.OsxApp {\n    public static void main(java.lang.String[]);\n}\n\n-dontwarn com.apple.eawt.**\n-keepattributes Signature\n\n-keep public interface org.jd.gui.api.** {*;}\n-keep public interface org.jd.gui.spi.** {*;}\n-keep public class * extends org.jd.gui.spi.*\n\n-keep class org.fife.ui.rsyntaxtextarea.TokenTypes {public static final <fields>;}\n-keep class org.fife.ui.rsyntaxtextarea.DefaultTokenMakerFactory\n\n-keep class org.fife.ui.rsyntaxtextarea.modes.CSSTokenMaker\n-keep class org.fife.ui.rsyntaxtextarea.modes.DtdTokenMaker\n-keep class org.fife.ui.rsyntaxtextarea.modes.HTMLTokenMaker\n-keep class org.fife.ui.rsyntaxtextarea.modes.JavaScriptTokenMaker\n-keep class org.fife.ui.rsyntaxtextarea.modes.JavaTokenMaker\n-keep class org.fife.ui.rsyntaxtextarea.modes.JsonTokenMaker\n-keep class org.fife.ui.rsyntaxtextarea.modes.JSPTokenMaker\n-keep class org.fife.ui.rsyntaxtextarea.modes.PlainTextTokenMaker\n-keep class org.fife.ui.rsyntaxtextarea.modes.PropertiesFileTokenMaker\n-keep class org.fife.ui.rsyntaxtextarea.modes.SQLTokenMaker\n-keep class org.fife.ui.rsyntaxtextarea.modes.XMLTokenMaker\n\n-adaptresourcefilenames ErrorStrip.properties\n-adaptresourcefilenames RSyntaxTextArea.properties\n-adaptresourcefilenames FocusableTip.properties\n"
  }
]